AccountManagerService.java revision 5cb2973495084f8ce3433b579e4b4962ed9d7efc
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accounts; 18 19import android.Manifest; 20import android.accounts.AbstractAccountAuthenticator; 21import android.accounts.Account; 22import android.accounts.AccountAndUser; 23import android.accounts.AccountAuthenticatorResponse; 24import android.accounts.AccountManager; 25import android.accounts.AccountManagerInternal; 26import android.accounts.AuthenticatorDescription; 27import android.accounts.CantAddAccountActivity; 28import android.accounts.GrantCredentialsPermissionActivity; 29import android.accounts.IAccountAuthenticator; 30import android.accounts.IAccountAuthenticatorResponse; 31import android.accounts.IAccountManager; 32import android.accounts.IAccountManagerResponse; 33import android.annotation.IntRange; 34import android.annotation.NonNull; 35import android.app.ActivityManager; 36import android.app.ActivityManagerNative; 37import android.app.ActivityThread; 38import android.app.AppGlobals; 39import android.app.AppOpsManager; 40import android.app.INotificationManager; 41import android.app.Notification; 42import android.app.NotificationManager; 43import android.app.PendingIntent; 44import android.app.admin.DeviceAdminInfo; 45import android.app.admin.DevicePolicyManager; 46import android.app.admin.DevicePolicyManagerInternal; 47import android.content.BroadcastReceiver; 48import android.content.ComponentName; 49import android.content.ContentValues; 50import android.content.Context; 51import android.content.Intent; 52import android.content.IntentFilter; 53import android.content.IntentSender; 54import android.content.ServiceConnection; 55import android.content.pm.ActivityInfo; 56import android.content.pm.ApplicationInfo; 57import android.content.pm.IPackageManager; 58import android.content.pm.PackageInfo; 59import android.content.pm.PackageManager; 60import android.content.pm.PackageManager.NameNotFoundException; 61import android.content.pm.RegisteredServicesCache; 62import android.content.pm.RegisteredServicesCacheListener; 63import android.content.pm.ResolveInfo; 64import android.content.pm.Signature; 65import android.content.pm.UserInfo; 66import android.database.Cursor; 67import android.database.DatabaseUtils; 68import android.database.sqlite.SQLiteDatabase; 69import android.database.sqlite.SQLiteOpenHelper; 70import android.database.sqlite.SQLiteStatement; 71import android.os.Binder; 72import android.os.Bundle; 73import android.os.Environment; 74import android.os.FileUtils; 75import android.os.Handler; 76import android.os.IBinder; 77import android.os.Looper; 78import android.os.Message; 79import android.os.Parcel; 80import android.os.Process; 81import android.os.RemoteCallback; 82import android.os.RemoteException; 83import android.os.ServiceManager; 84import android.os.SystemClock; 85import android.os.UserHandle; 86import android.os.UserManager; 87import android.os.storage.StorageManager; 88import android.service.notification.StatusBarNotification; 89import android.text.TextUtils; 90import android.util.Log; 91import android.util.Pair; 92import android.util.Slog; 93import android.util.SparseArray; 94import android.util.SparseBooleanArray; 95 96import com.android.internal.R; 97import com.android.internal.annotations.VisibleForTesting; 98import com.android.internal.content.PackageMonitor; 99import com.android.internal.util.ArrayUtils; 100import com.android.internal.util.IndentingPrintWriter; 101import com.android.internal.util.Preconditions; 102import com.android.server.FgThread; 103import com.android.server.LocalServices; 104import com.android.server.SystemService; 105 106import com.google.android.collect.Lists; 107import com.google.android.collect.Sets; 108 109import java.io.File; 110import java.io.FileDescriptor; 111import java.io.IOException; 112import java.io.PrintWriter; 113import java.security.GeneralSecurityException; 114import java.security.MessageDigest; 115import java.security.NoSuchAlgorithmException; 116import java.text.SimpleDateFormat; 117import java.util.ArrayList; 118import java.util.Arrays; 119import java.util.Collection; 120import java.util.Date; 121import java.util.HashMap; 122import java.util.HashSet; 123import java.util.Iterator; 124import java.util.LinkedHashMap; 125import java.util.List; 126import java.util.Map; 127import java.util.Map.Entry; 128import java.util.concurrent.atomic.AtomicInteger; 129import java.util.concurrent.atomic.AtomicReference; 130 131/** 132 * A system service that provides account, password, and authtoken management for all 133 * accounts on the device. Some of these calls are implemented with the help of the corresponding 134 * {@link IAccountAuthenticator} services. This service is not accessed by users directly, 135 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: 136 * AccountManager accountManager = AccountManager.get(context); 137 * @hide 138 */ 139public class AccountManagerService 140 extends IAccountManager.Stub 141 implements RegisteredServicesCacheListener<AuthenticatorDescription> { 142 private static final String TAG = "AccountManagerService"; 143 144 public static class Lifecycle extends SystemService { 145 private AccountManagerService mService; 146 147 public Lifecycle(Context context) { 148 super(context); 149 } 150 151 @Override 152 public void onStart() { 153 mService = new AccountManagerService(getContext()); 154 publishBinderService(Context.ACCOUNT_SERVICE, mService); 155 } 156 157 @Override 158 public void onBootPhase(int phase) { 159 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 160 mService.systemReady(); 161 } 162 } 163 164 @Override 165 public void onUnlockUser(int userHandle) { 166 mService.onUnlockUser(userHandle); 167 } 168 } 169 170 private static final String DATABASE_NAME = "accounts.db"; 171 private static final int PRE_N_DATABASE_VERSION = 9; 172 private static final int CE_DATABASE_VERSION = 10; 173 private static final int DE_DATABASE_VERSION = 1; 174 175 private static final int MAX_DEBUG_DB_SIZE = 64; 176 177 private final Context mContext; 178 179 private final PackageManager mPackageManager; 180 private final AppOpsManager mAppOpsManager; 181 private UserManager mUserManager; 182 183 private final MessageHandler mMessageHandler; 184 185 // Messages that can be sent on mHandler 186 private static final int MESSAGE_TIMED_OUT = 3; 187 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; 188 189 private final IAccountAuthenticatorCache mAuthenticatorCache; 190 191 private static final String TABLE_ACCOUNTS = "accounts"; 192 private static final String ACCOUNTS_ID = "_id"; 193 private static final String ACCOUNTS_NAME = "name"; 194 private static final String ACCOUNTS_TYPE = "type"; 195 private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; 196 private static final String ACCOUNTS_PASSWORD = "password"; 197 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name"; 198 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS = 199 "last_password_entry_time_millis_epoch"; 200 201 private static final String TABLE_AUTHTOKENS = "authtokens"; 202 private static final String AUTHTOKENS_ID = "_id"; 203 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; 204 private static final String AUTHTOKENS_TYPE = "type"; 205 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; 206 207 private static final String TABLE_GRANTS = "grants"; 208 private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; 209 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; 210 private static final String GRANTS_GRANTEE_UID = "uid"; 211 212 private static final String TABLE_EXTRAS = "extras"; 213 private static final String EXTRAS_ID = "_id"; 214 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; 215 private static final String EXTRAS_KEY = "key"; 216 private static final String EXTRAS_VALUE = "value"; 217 218 private static final String TABLE_META = "meta"; 219 private static final String META_KEY = "key"; 220 private static final String META_VALUE = "value"; 221 222 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts"; 223 private static final String SHARED_ACCOUNTS_ID = "_id"; 224 225 private static final String PRE_N_DATABASE_NAME = "accounts.db"; 226 private static final String CE_DATABASE_NAME = "accounts_ce.db"; 227 private static final String DE_DATABASE_NAME = "accounts_de.db"; 228 private static final String CE_DB_PREFIX = "ceDb."; 229 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS; 230 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS; 231 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS; 232 233 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = 234 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; 235 private static final Intent ACCOUNTS_CHANGED_INTENT; 236 237 static { 238 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); 239 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 240 } 241 242 private static final String COUNT_OF_MATCHING_GRANTS = "" 243 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 244 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 245 + " AND " + GRANTS_GRANTEE_UID + "=?" 246 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" 247 + " AND " + ACCOUNTS_NAME + "=?" 248 + " AND " + ACCOUNTS_TYPE + "=?"; 249 250 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = "" 251 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 252 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 253 + " AND " + GRANTS_GRANTEE_UID + "=?" 254 + " AND " + ACCOUNTS_NAME + "=?" 255 + " AND " + ACCOUNTS_TYPE + "=?"; 256 257 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = 258 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 259 260 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, 261 AUTHTOKENS_AUTHTOKEN}; 262 263 private static final String SELECTION_USERDATA_BY_ACCOUNT = 264 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 265 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; 266 267 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX = 268 "auth_uid_for_type:"; 269 private static final String META_KEY_DELIMITER = ":"; 270 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?"; 271 272 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); 273 private final AtomicInteger mNotificationIds = new AtomicInteger(1); 274 275 static class UserAccounts { 276 private final int userId; 277 private final DeDatabaseHelper openHelper; 278 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> 279 credentialsPermissionNotificationIds = 280 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); 281 private final HashMap<Account, Integer> signinRequiredNotificationIds = 282 new HashMap<Account, Integer>(); 283 private final Object cacheLock = new Object(); 284 /** protected by the {@link #cacheLock} */ 285 private final HashMap<String, Account[]> accountCache = 286 new LinkedHashMap<String, Account[]>(); 287 /** protected by the {@link #cacheLock} */ 288 private final HashMap<Account, HashMap<String, String>> userDataCache = 289 new HashMap<Account, HashMap<String, String>>(); 290 /** protected by the {@link #cacheLock} */ 291 private final HashMap<Account, HashMap<String, String>> authTokenCache = 292 new HashMap<Account, HashMap<String, String>>(); 293 294 /** protected by the {@link #cacheLock} */ 295 private final TokenCache accountTokenCaches = new TokenCache(); 296 297 /** 298 * protected by the {@link #cacheLock} 299 * 300 * Caches the previous names associated with an account. Previous names 301 * should be cached because we expect that when an Account is renamed, 302 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and 303 * want to know if the accounts they care about have been renamed. 304 * 305 * The previous names are wrapped in an {@link AtomicReference} so that 306 * we can distinguish between those accounts with no previous names and 307 * those whose previous names haven't been cached (yet). 308 */ 309 private final HashMap<Account, AtomicReference<String>> previousNameCache = 310 new HashMap<Account, AtomicReference<String>>(); 311 312 private int debugDbInsertionPoint = -1; 313 private SQLiteStatement statementForLogging; 314 315 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) { 316 this.userId = userId; 317 synchronized (cacheLock) { 318 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile); 319 } 320 } 321 } 322 323 private final SparseArray<UserAccounts> mUsers = new SparseArray<>(); 324 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray(); 325 326 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>(); 327 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; 328 329 /** 330 * This should only be called by system code. One should only call this after the service 331 * has started. 332 * @return a reference to the AccountManagerService instance 333 * @hide 334 */ 335 public static AccountManagerService getSingleton() { 336 return sThis.get(); 337 } 338 339 public AccountManagerService(Context context) { 340 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); 341 } 342 343 public AccountManagerService(Context context, PackageManager packageManager, 344 IAccountAuthenticatorCache authenticatorCache) { 345 mContext = context; 346 mPackageManager = packageManager; 347 mAppOpsManager = mContext.getSystemService(AppOpsManager.class); 348 349 mMessageHandler = new MessageHandler(FgThread.get().getLooper()); 350 351 mAuthenticatorCache = authenticatorCache; 352 mAuthenticatorCache.setListener(this, null /* Handler */); 353 354 sThis.set(this); 355 356 IntentFilter intentFilter = new IntentFilter(); 357 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 358 intentFilter.addDataScheme("package"); 359 mContext.registerReceiver(new BroadcastReceiver() { 360 @Override 361 public void onReceive(Context context1, Intent intent) { 362 // Don't delete accounts when updating a authenticator's 363 // package. 364 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 365 /* Purging data requires file io, don't block the main thread. This is probably 366 * less than ideal because we are introducing a race condition where old grants 367 * could be exercised until they are purged. But that race condition existed 368 * anyway with the broadcast receiver. 369 * 370 * Ideally, we would completely clear the cache, purge data from the database, 371 * and then rebuild the cache. All under the cache lock. But that change is too 372 * large at this point. 373 */ 374 Runnable r = new Runnable() { 375 @Override 376 public void run() { 377 purgeOldGrantsAll(); 378 } 379 }; 380 new Thread(r).start(); 381 } 382 } 383 }, intentFilter); 384 385 IntentFilter userFilter = new IntentFilter(); 386 userFilter.addAction(Intent.ACTION_USER_REMOVED); 387 mContext.registerReceiverAsUser(new BroadcastReceiver() { 388 @Override 389 public void onReceive(Context context, Intent intent) { 390 String action = intent.getAction(); 391 if (Intent.ACTION_USER_REMOVED.equals(action)) { 392 onUserRemoved(intent); 393 } 394 } 395 }, UserHandle.ALL, userFilter, null, null); 396 397 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl()); 398 399 // Need to cancel account request notifications if the update/install can access the account 400 new PackageMonitor() { 401 @Override 402 public void onPackageAdded(String packageName, int uid) { 403 // Called on a handler, and running as the system 404 cancelAccountAccessRequestNotificationIfNeeded(uid, true); 405 } 406 407 @Override 408 public void onPackageUpdateFinished(String packageName, int uid) { 409 // Called on a handler, and running as the system 410 cancelAccountAccessRequestNotificationIfNeeded(uid, true); 411 } 412 }.register(mContext, mMessageHandler.getLooper(), UserHandle.ALL, true); 413 414 // Cancel account request notification if an app op was preventing the account access 415 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null, 416 new AppOpsManager.OnOpChangedInternalListener() { 417 @Override 418 public void onOpChanged(int op, String packageName) { 419 try { 420 final int userId = ActivityManager.getCurrentUser(); 421 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); 422 final int mode = mAppOpsManager.checkOpNoThrow( 423 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName); 424 if (mode == AppOpsManager.MODE_ALLOWED) { 425 final long identity = Binder.clearCallingIdentity(); 426 try { 427 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true); 428 } finally { 429 Binder.restoreCallingIdentity(identity); 430 } 431 } 432 } catch (NameNotFoundException e) { 433 /* ignore */ 434 } 435 } 436 }); 437 438 // Cancel account request notification if a permission was preventing the account access 439 mPackageManager.addOnPermissionsChangeListener( 440 (int uid) -> { 441 Account[] accounts = null; 442 String[] packageNames = mPackageManager.getPackagesForUid(uid); 443 if (packageNames != null) { 444 final int userId = UserHandle.getUserId(uid); 445 final long identity = Binder.clearCallingIdentity(); 446 try { 447 for (String packageName : packageNames) { 448 if (mContext.getPackageManager().checkPermission( 449 Manifest.permission.GET_ACCOUNTS, packageName) 450 != PackageManager.PERMISSION_GRANTED) { 451 continue; 452 } 453 454 if (accounts == null) { 455 accounts = getAccountsAsUser(null, userId, "android"); 456 if (ArrayUtils.isEmpty(accounts)) { 457 return; 458 } 459 } 460 461 for (Account account : accounts) { 462 cancelAccountAccessRequestNotificationIfNeeded( 463 account, uid, packageName, true); 464 } 465 } 466 } finally { 467 Binder.restoreCallingIdentity(identity); 468 } 469 } 470 }); 471 } 472 473 private void cancelAccountAccessRequestNotificationIfNeeded(int uid, 474 boolean checkAccess) { 475 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); 476 for (Account account : accounts) { 477 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess); 478 } 479 } 480 481 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid, 482 boolean checkAccess) { 483 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); 484 for (Account account : accounts) { 485 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess); 486 } 487 } 488 489 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid, 490 boolean checkAccess) { 491 String[] packageNames = mPackageManager.getPackagesForUid(uid); 492 if (packageNames != null) { 493 for (String packageName : packageNames) { 494 cancelAccountAccessRequestNotificationIfNeeded(account, uid, 495 packageName, checkAccess); 496 } 497 } 498 } 499 500 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, 501 int uid, String packageName, boolean checkAccess) { 502 if (!checkAccess || hasAccountAccess(account, packageName, 503 UserHandle.getUserHandleForUid(uid))) { 504 cancelNotification(getCredentialPermissionNotificationId(account, 505 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, 506 UserHandle.getUserHandleForUid(uid)); 507 } 508 } 509 510 @Override 511 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 512 throws RemoteException { 513 try { 514 return super.onTransact(code, data, reply, flags); 515 } catch (RuntimeException e) { 516 // The account manager only throws security exceptions, so let's 517 // log all others. 518 if (!(e instanceof SecurityException)) { 519 Slog.wtf(TAG, "Account Manager Crash", e); 520 } 521 throw e; 522 } 523 } 524 525 public void systemReady() { 526 } 527 528 private UserManager getUserManager() { 529 if (mUserManager == null) { 530 mUserManager = UserManager.get(mContext); 531 } 532 return mUserManager; 533 } 534 535 /** 536 * Validate internal set of accounts against installed authenticators for 537 * given user. Clears cached authenticators before validating. 538 */ 539 public void validateAccounts(int userId) { 540 final UserAccounts accounts = getUserAccounts(userId); 541 // Invalidate user-specific cache to make sure we catch any 542 // removed authenticators. 543 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 544 } 545 546 /** 547 * Validate internal set of accounts against installed authenticators for 548 * given user. Clear cached authenticators before validating when requested. 549 */ 550 private void validateAccountsInternal( 551 UserAccounts accounts, boolean invalidateAuthenticatorCache) { 552 if (Log.isLoggable(TAG, Log.DEBUG)) { 553 Log.d(TAG, "validateAccountsInternal " + accounts.userId 554 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached() 555 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId)); 556 } 557 558 if (invalidateAuthenticatorCache) { 559 mAuthenticatorCache.invalidateCache(accounts.userId); 560 } 561 562 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser( 563 mAuthenticatorCache, accounts.userId); 564 boolean userUnlocked = isLocalUnlockedUser(accounts.userId); 565 566 synchronized (accounts.cacheLock) { 567 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 568 boolean accountDeleted = false; 569 570 // Get a list of stored authenticator type and UID 571 Cursor metaCursor = db.query( 572 TABLE_META, 573 new String[] {META_KEY, META_VALUE}, 574 SELECTION_META_BY_AUTHENTICATOR_TYPE, 575 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"}, 576 null /* groupBy */, 577 null /* having */, 578 META_KEY); 579 // Create a list of authenticator type whose previous uid no longer exists 580 HashSet<String> obsoleteAuthType = Sets.newHashSet(); 581 try { 582 SparseBooleanArray knownUids = null; 583 while (metaCursor.moveToNext()) { 584 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1]; 585 String uid = metaCursor.getString(1); 586 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) { 587 // Should never happen. 588 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type) 589 + ", uid empty: " + TextUtils.isEmpty(uid)); 590 continue; 591 } 592 Integer knownUid = knownAuth.get(type); 593 if (knownUid != null && uid.equals(knownUid.toString())) { 594 // Remove it from the knownAuth list if it's unchanged. 595 knownAuth.remove(type); 596 } else { 597 /* 598 * The authenticator is presently not cached and should only be triggered 599 * when we think an authenticator has been removed (or is being updated). 600 * But we still want to check if any data with the associated uid is 601 * around. This is an (imperfect) signal that the package may be updating. 602 * 603 * A side effect of this is that an authenticator sharing a uid with 604 * multiple apps won't get its credentials wiped as long as some app with 605 * that uid is still on the device. But I suspect that this is a rare case. 606 * And it isn't clear to me how an attacker could really exploit that 607 * feature. 608 * 609 * The upshot is that we don't have to worry about accounts getting 610 * uninstalled while the authenticator's package is being updated. 611 * 612 */ 613 if (knownUids == null) { 614 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId); 615 } 616 if (!knownUids.get(Integer.parseInt(uid))) { 617 // The authenticator is not presently available to the cache. And the 618 // package no longer has a data directory (so we surmise it isn't updating). 619 // So purge its data from the account databases. 620 obsoleteAuthType.add(type); 621 // And delete it from the TABLE_META 622 db.delete( 623 TABLE_META, 624 META_KEY + "=? AND " + META_VALUE + "=?", 625 new String[] { 626 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type, 627 uid} 628 ); 629 } 630 } 631 } 632 } finally { 633 metaCursor.close(); 634 } 635 636 // Add the newly registered authenticator to TABLE_META. If old authenticators have 637 // been renabled (after being updated for example), then we just overwrite the old 638 // values. 639 Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator(); 640 while (iterator.hasNext()) { 641 Entry<String, Integer> entry = iterator.next(); 642 ContentValues values = new ContentValues(); 643 values.put(META_KEY, 644 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey()); 645 values.put(META_VALUE, entry.getValue()); 646 db.insertWithOnConflict(TABLE_META, null, values, SQLiteDatabase.CONFLICT_REPLACE); 647 } 648 649 Cursor cursor = db.query(TABLE_ACCOUNTS, 650 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, 651 null, null, null, null, ACCOUNTS_ID); 652 try { 653 accounts.accountCache.clear(); 654 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>(); 655 while (cursor.moveToNext()) { 656 final long accountId = cursor.getLong(0); 657 final String accountType = cursor.getString(1); 658 final String accountName = cursor.getString(2); 659 660 if (obsoleteAuthType.contains(accountType)) { 661 Slog.w(TAG, "deleting account " + accountName + " because type " 662 + accountType + "'s registered authenticator no longer exist."); 663 db.beginTransaction(); 664 try { 665 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); 666 // Also delete from CE table if user is unlocked; if user is currently 667 // locked the account will be removed later by syncDeCeAccountsLocked 668 if (userUnlocked) { 669 db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); 670 } 671 db.setTransactionSuccessful(); 672 } finally { 673 db.endTransaction(); 674 } 675 accountDeleted = true; 676 677 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS, 678 accountId, accounts); 679 680 final Account account = new Account(accountName, accountType); 681 accounts.userDataCache.remove(account); 682 accounts.authTokenCache.remove(account); 683 accounts.accountTokenCaches.remove(account); 684 } else { 685 ArrayList<String> accountNames = accountNamesByType.get(accountType); 686 if (accountNames == null) { 687 accountNames = new ArrayList<String>(); 688 accountNamesByType.put(accountType, accountNames); 689 } 690 accountNames.add(accountName); 691 } 692 } 693 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) { 694 final String accountType = cur.getKey(); 695 final ArrayList<String> accountNames = cur.getValue(); 696 final Account[] accountsForType = new Account[accountNames.size()]; 697 for (int i = 0; i < accountsForType.length; i++) { 698 accountsForType[i] = new Account(accountNames.get(i), accountType); 699 } 700 accounts.accountCache.put(accountType, accountsForType); 701 } 702 } finally { 703 cursor.close(); 704 if (accountDeleted) { 705 sendAccountsChangedBroadcast(accounts.userId); 706 } 707 } 708 } 709 } 710 711 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) { 712 // Get the UIDs of all apps that might have data on the device. We want 713 // to preserve user data if the app might otherwise be storing data. 714 List<PackageInfo> pkgsWithData = 715 mPackageManager.getInstalledPackagesAsUser( 716 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 717 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size()); 718 for (PackageInfo pkgInfo : pkgsWithData) { 719 if (pkgInfo.applicationInfo != null 720 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 721 knownUids.put(pkgInfo.applicationInfo.uid, true); 722 } 723 } 724 return knownUids; 725 } 726 727 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser( 728 Context context, 729 int userId) { 730 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context); 731 return getAuthenticatorTypeAndUIDForUser(authCache, userId); 732 } 733 734 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser( 735 IAccountAuthenticatorCache authCache, 736 int userId) { 737 HashMap<String, Integer> knownAuth = new HashMap<>(); 738 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache 739 .getAllServices(userId)) { 740 knownAuth.put(service.type.type, service.uid); 741 } 742 return knownAuth; 743 } 744 745 private UserAccounts getUserAccountsForCaller() { 746 return getUserAccounts(UserHandle.getCallingUserId()); 747 } 748 749 protected UserAccounts getUserAccounts(int userId) { 750 synchronized (mUsers) { 751 UserAccounts accounts = mUsers.get(userId); 752 boolean validateAccounts = false; 753 if (accounts == null) { 754 File preNDbFile = new File(getPreNDatabaseName(userId)); 755 File deDbFile = new File(getDeDatabaseName(userId)); 756 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile); 757 initializeDebugDbSizeAndCompileSqlStatementForLogging( 758 accounts.openHelper.getWritableDatabase(), accounts); 759 mUsers.append(userId, accounts); 760 purgeOldGrants(accounts); 761 validateAccounts = true; 762 } 763 // open CE database if necessary 764 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) { 765 Log.i(TAG, "User " + userId + " is unlocked - opening CE database"); 766 synchronized (accounts.cacheLock) { 767 File preNDatabaseFile = new File(getPreNDatabaseName(userId)); 768 File ceDatabaseFile = new File(getCeDatabaseName(userId)); 769 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile); 770 accounts.openHelper.attachCeDatabase(ceDatabaseFile); 771 } 772 syncDeCeAccountsLocked(accounts); 773 } 774 if (validateAccounts) { 775 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 776 } 777 return accounts; 778 } 779 } 780 781 private void syncDeCeAccountsLocked(UserAccounts accounts) { 782 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held"); 783 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 784 List<Account> accountsToRemove = CeDatabaseHelper.findCeAccountsNotInDe(db); 785 if (!accountsToRemove.isEmpty()) { 786 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user " 787 + accounts.userId + " was locked. Removing accounts from CE tables"); 788 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS); 789 790 for (Account account : accountsToRemove) { 791 removeAccountInternal(accounts, account, Process.myUid()); 792 } 793 } 794 } 795 796 private void purgeOldGrantsAll() { 797 synchronized (mUsers) { 798 for (int i = 0; i < mUsers.size(); i++) { 799 purgeOldGrants(mUsers.valueAt(i)); 800 } 801 } 802 } 803 804 private void purgeOldGrants(UserAccounts accounts) { 805 synchronized (accounts.cacheLock) { 806 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 807 final Cursor cursor = db.query(TABLE_GRANTS, 808 new String[]{GRANTS_GRANTEE_UID}, 809 null, null, GRANTS_GRANTEE_UID, null, null); 810 try { 811 while (cursor.moveToNext()) { 812 final int uid = cursor.getInt(0); 813 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; 814 if (packageExists) { 815 continue; 816 } 817 Log.d(TAG, "deleting grants for UID " + uid 818 + " because its package is no longer installed"); 819 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", 820 new String[]{Integer.toString(uid)}); 821 } 822 } finally { 823 cursor.close(); 824 } 825 } 826 } 827 828 private void onUserRemoved(Intent intent) { 829 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 830 if (userId < 1) return; 831 832 UserAccounts accounts; 833 boolean userUnlocked; 834 synchronized (mUsers) { 835 accounts = mUsers.get(userId); 836 mUsers.remove(userId); 837 userUnlocked = mLocalUnlockedUsers.get(userId); 838 mLocalUnlockedUsers.delete(userId); 839 } 840 if (accounts != null) { 841 synchronized (accounts.cacheLock) { 842 accounts.openHelper.close(); 843 } 844 } 845 Log.i(TAG, "Removing database files for user " + userId); 846 File dbFile = new File(getDeDatabaseName(userId)); 847 848 deleteDbFileWarnIfFailed(dbFile); 849 // Remove CE file if user is unlocked, or FBE is not enabled 850 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated(); 851 if (!fbeEnabled || userUnlocked) { 852 File ceDb = new File(getCeDatabaseName(userId)); 853 if (ceDb.exists()) { 854 deleteDbFileWarnIfFailed(ceDb); 855 } 856 } 857 } 858 859 private static void deleteDbFileWarnIfFailed(File dbFile) { 860 if (!SQLiteDatabase.deleteDatabase(dbFile)) { 861 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully"); 862 } 863 } 864 865 @VisibleForTesting 866 void onUserUnlocked(Intent intent) { 867 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); 868 } 869 870 void onUnlockUser(int userId) { 871 if (Log.isLoggable(TAG, Log.VERBOSE)) { 872 Log.v(TAG, "onUserUnlocked " + userId); 873 } 874 synchronized (mUsers) { 875 mLocalUnlockedUsers.put(userId, true); 876 } 877 if (userId < 1) return; 878 syncSharedAccounts(userId); 879 } 880 881 private void syncSharedAccounts(int userId) { 882 // Check if there's a shared account that needs to be created as an account 883 Account[] sharedAccounts = getSharedAccountsAsUser(userId); 884 if (sharedAccounts == null || sharedAccounts.length == 0) return; 885 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName()); 886 int parentUserId = UserManager.isSplitSystemUser() 887 ? getUserManager().getUserInfo(userId).restrictedProfileParentId 888 : UserHandle.USER_SYSTEM; 889 if (parentUserId < 0) { 890 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user"); 891 return; 892 } 893 for (Account sa : sharedAccounts) { 894 if (ArrayUtils.contains(accounts, sa)) continue; 895 // Account doesn't exist. Copy it now. 896 copyAccountToUser(null /*no response*/, sa, parentUserId, userId); 897 } 898 } 899 900 @Override 901 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { 902 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); 903 } 904 905 @Override 906 public String getPassword(Account account) { 907 int callingUid = Binder.getCallingUid(); 908 if (Log.isLoggable(TAG, Log.VERBOSE)) { 909 Log.v(TAG, "getPassword: " + account 910 + ", caller's uid " + Binder.getCallingUid() 911 + ", pid " + Binder.getCallingPid()); 912 } 913 if (account == null) throw new IllegalArgumentException("account is null"); 914 int userId = UserHandle.getCallingUserId(); 915 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 916 String msg = String.format( 917 "uid %s cannot get secrets for accounts of type: %s", 918 callingUid, 919 account.type); 920 throw new SecurityException(msg); 921 } 922 long identityToken = clearCallingIdentity(); 923 try { 924 UserAccounts accounts = getUserAccounts(userId); 925 return readPasswordInternal(accounts, account); 926 } finally { 927 restoreCallingIdentity(identityToken); 928 } 929 } 930 931 private String readPasswordInternal(UserAccounts accounts, Account account) { 932 if (account == null) { 933 return null; 934 } 935 if (!isLocalUnlockedUser(accounts.userId)) { 936 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked"); 937 return null; 938 } 939 940 synchronized (accounts.cacheLock) { 941 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 942 return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name, 943 account.type); 944 } 945 } 946 947 @Override 948 public String getPreviousName(Account account) { 949 if (Log.isLoggable(TAG, Log.VERBOSE)) { 950 Log.v(TAG, "getPreviousName: " + account 951 + ", caller's uid " + Binder.getCallingUid() 952 + ", pid " + Binder.getCallingPid()); 953 } 954 if (account == null) throw new IllegalArgumentException("account is null"); 955 int userId = UserHandle.getCallingUserId(); 956 long identityToken = clearCallingIdentity(); 957 try { 958 UserAccounts accounts = getUserAccounts(userId); 959 return readPreviousNameInternal(accounts, account); 960 } finally { 961 restoreCallingIdentity(identityToken); 962 } 963 } 964 965 private String readPreviousNameInternal(UserAccounts accounts, Account account) { 966 if (account == null) { 967 return null; 968 } 969 synchronized (accounts.cacheLock) { 970 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account); 971 if (previousNameRef == null) { 972 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 973 Cursor cursor = db.query( 974 TABLE_ACCOUNTS, 975 new String[]{ ACCOUNTS_PREVIOUS_NAME }, 976 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 977 new String[] { account.name, account.type }, 978 null, 979 null, 980 null); 981 try { 982 if (cursor.moveToNext()) { 983 String previousName = cursor.getString(0); 984 previousNameRef = new AtomicReference<>(previousName); 985 accounts.previousNameCache.put(account, previousNameRef); 986 return previousName; 987 } else { 988 return null; 989 } 990 } finally { 991 cursor.close(); 992 } 993 } else { 994 return previousNameRef.get(); 995 } 996 } 997 } 998 999 @Override 1000 public String getUserData(Account account, String key) { 1001 final int callingUid = Binder.getCallingUid(); 1002 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1003 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s", 1004 account, key, callingUid, Binder.getCallingPid()); 1005 Log.v(TAG, msg); 1006 } 1007 if (account == null) throw new IllegalArgumentException("account is null"); 1008 if (key == null) throw new IllegalArgumentException("key is null"); 1009 int userId = UserHandle.getCallingUserId(); 1010 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1011 String msg = String.format( 1012 "uid %s cannot get user data for accounts of type: %s", 1013 callingUid, 1014 account.type); 1015 throw new SecurityException(msg); 1016 } 1017 if (!isLocalUnlockedUser(userId)) { 1018 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid); 1019 return null; 1020 } 1021 long identityToken = clearCallingIdentity(); 1022 try { 1023 UserAccounts accounts = getUserAccounts(userId); 1024 synchronized (accounts.cacheLock) { 1025 if (!accountExistsCacheLocked(accounts, account)) { 1026 return null; 1027 } 1028 return readUserDataInternalLocked(accounts, account, key); 1029 } 1030 } finally { 1031 restoreCallingIdentity(identityToken); 1032 } 1033 } 1034 1035 @Override 1036 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) { 1037 int callingUid = Binder.getCallingUid(); 1038 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1039 Log.v(TAG, "getAuthenticatorTypes: " 1040 + "for user id " + userId 1041 + " caller's uid " + callingUid 1042 + ", pid " + Binder.getCallingPid()); 1043 } 1044 // Only allow the system process to read accounts of other users 1045 if (isCrossUser(callingUid, userId)) { 1046 throw new SecurityException( 1047 String.format( 1048 "User %s tying to get authenticator types for %s" , 1049 UserHandle.getCallingUserId(), 1050 userId)); 1051 } 1052 1053 final long identityToken = clearCallingIdentity(); 1054 try { 1055 return getAuthenticatorTypesInternal(userId); 1056 1057 } finally { 1058 restoreCallingIdentity(identityToken); 1059 } 1060 } 1061 1062 /** 1063 * Should only be called inside of a clearCallingIdentity block. 1064 */ 1065 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) { 1066 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> 1067 authenticatorCollection = mAuthenticatorCache.getAllServices(userId); 1068 AuthenticatorDescription[] types = 1069 new AuthenticatorDescription[authenticatorCollection.size()]; 1070 int i = 0; 1071 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator 1072 : authenticatorCollection) { 1073 types[i] = authenticator.type; 1074 i++; 1075 } 1076 return types; 1077 } 1078 1079 1080 1081 private boolean isCrossUser(int callingUid, int userId) { 1082 return (userId != UserHandle.getCallingUserId() 1083 && callingUid != Process.myUid() 1084 && mContext.checkCallingOrSelfPermission( 1085 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 1086 != PackageManager.PERMISSION_GRANTED); 1087 } 1088 1089 @Override 1090 public boolean addAccountExplicitly(Account account, String password, Bundle extras) { 1091 Bundle.setDefusable(extras, true); 1092 final int callingUid = Binder.getCallingUid(); 1093 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1094 Log.v(TAG, "addAccountExplicitly: " + account 1095 + ", caller's uid " + callingUid 1096 + ", pid " + Binder.getCallingPid()); 1097 } 1098 if (account == null) throw new IllegalArgumentException("account is null"); 1099 int userId = UserHandle.getCallingUserId(); 1100 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1101 String msg = String.format( 1102 "uid %s cannot explicitly add accounts of type: %s", 1103 callingUid, 1104 account.type); 1105 throw new SecurityException(msg); 1106 } 1107 /* 1108 * Child users are not allowed to add accounts. Only the accounts that are 1109 * shared by the parent profile can be added to child profile. 1110 * 1111 * TODO: Only allow accounts that were shared to be added by 1112 * a limited user. 1113 */ 1114 1115 // fails if the account already exists 1116 long identityToken = clearCallingIdentity(); 1117 try { 1118 UserAccounts accounts = getUserAccounts(userId); 1119 return addAccountInternal(accounts, account, password, extras, callingUid); 1120 } finally { 1121 restoreCallingIdentity(identityToken); 1122 } 1123 } 1124 1125 @Override 1126 public void copyAccountToUser(final IAccountManagerResponse response, final Account account, 1127 final int userFrom, int userTo) { 1128 int callingUid = Binder.getCallingUid(); 1129 if (isCrossUser(callingUid, UserHandle.USER_ALL)) { 1130 throw new SecurityException("Calling copyAccountToUser requires " 1131 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); 1132 } 1133 final UserAccounts fromAccounts = getUserAccounts(userFrom); 1134 final UserAccounts toAccounts = getUserAccounts(userTo); 1135 if (fromAccounts == null || toAccounts == null) { 1136 if (response != null) { 1137 Bundle result = new Bundle(); 1138 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); 1139 try { 1140 response.onResult(result); 1141 } catch (RemoteException e) { 1142 Slog.w(TAG, "Failed to report error back to the client." + e); 1143 } 1144 } 1145 return; 1146 } 1147 1148 Slog.d(TAG, "Copying account " + account.name 1149 + " from user " + userFrom + " to user " + userTo); 1150 long identityToken = clearCallingIdentity(); 1151 try { 1152 new Session(fromAccounts, response, account.type, false, 1153 false /* stripAuthTokenFromResult */, account.name, 1154 false /* authDetailsRequired */) { 1155 @Override 1156 protected String toDebugString(long now) { 1157 return super.toDebugString(now) + ", getAccountCredentialsForClone" 1158 + ", " + account.type; 1159 } 1160 1161 @Override 1162 public void run() throws RemoteException { 1163 mAuthenticator.getAccountCredentialsForCloning(this, account); 1164 } 1165 1166 @Override 1167 public void onResult(Bundle result) { 1168 Bundle.setDefusable(result, true); 1169 if (result != null 1170 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 1171 // Create a Session for the target user and pass in the bundle 1172 completeCloningAccount(response, result, account, toAccounts, userFrom); 1173 } else { 1174 super.onResult(result); 1175 } 1176 } 1177 }.bind(); 1178 } finally { 1179 restoreCallingIdentity(identityToken); 1180 } 1181 } 1182 1183 @Override 1184 public boolean accountAuthenticated(final Account account) { 1185 final int callingUid = Binder.getCallingUid(); 1186 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1187 String msg = String.format( 1188 "accountAuthenticated( account: %s, callerUid: %s)", 1189 account, 1190 callingUid); 1191 Log.v(TAG, msg); 1192 } 1193 if (account == null) { 1194 throw new IllegalArgumentException("account is null"); 1195 } 1196 int userId = UserHandle.getCallingUserId(); 1197 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1198 String msg = String.format( 1199 "uid %s cannot notify authentication for accounts of type: %s", 1200 callingUid, 1201 account.type); 1202 throw new SecurityException(msg); 1203 } 1204 1205 if (!canUserModifyAccounts(userId, callingUid) || 1206 !canUserModifyAccountsForType(userId, account.type, callingUid)) { 1207 return false; 1208 } 1209 1210 long identityToken = clearCallingIdentity(); 1211 try { 1212 UserAccounts accounts = getUserAccounts(userId); 1213 return updateLastAuthenticatedTime(account); 1214 } finally { 1215 restoreCallingIdentity(identityToken); 1216 } 1217 } 1218 1219 private boolean updateLastAuthenticatedTime(Account account) { 1220 final UserAccounts accounts = getUserAccountsForCaller(); 1221 synchronized (accounts.cacheLock) { 1222 final ContentValues values = new ContentValues(); 1223 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis()); 1224 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1225 int i = db.update( 1226 TABLE_ACCOUNTS, 1227 values, 1228 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 1229 new String[] { 1230 account.name, account.type 1231 }); 1232 if (i > 0) { 1233 return true; 1234 } 1235 } 1236 return false; 1237 } 1238 1239 private void completeCloningAccount(IAccountManagerResponse response, 1240 final Bundle accountCredentials, final Account account, final UserAccounts targetUser, 1241 final int parentUserId){ 1242 Bundle.setDefusable(accountCredentials, true); 1243 long id = clearCallingIdentity(); 1244 try { 1245 new Session(targetUser, response, account.type, false, 1246 false /* stripAuthTokenFromResult */, account.name, 1247 false /* authDetailsRequired */) { 1248 @Override 1249 protected String toDebugString(long now) { 1250 return super.toDebugString(now) + ", getAccountCredentialsForClone" 1251 + ", " + account.type; 1252 } 1253 1254 @Override 1255 public void run() throws RemoteException { 1256 // Confirm that the owner's account still exists before this step. 1257 UserAccounts owner = getUserAccounts(parentUserId); 1258 synchronized (owner.cacheLock) { 1259 for (Account acc : getAccounts(parentUserId, 1260 mContext.getOpPackageName())) { 1261 if (acc.equals(account)) { 1262 mAuthenticator.addAccountFromCredentials( 1263 this, account, accountCredentials); 1264 break; 1265 } 1266 } 1267 } 1268 } 1269 1270 @Override 1271 public void onResult(Bundle result) { 1272 Bundle.setDefusable(result, true); 1273 // TODO: Anything to do if if succedded? 1274 // TODO: If it failed: Show error notification? Should we remove the shadow 1275 // account to avoid retries? 1276 super.onResult(result); 1277 } 1278 1279 @Override 1280 public void onError(int errorCode, String errorMessage) { 1281 super.onError(errorCode, errorMessage); 1282 // TODO: Show error notification to user 1283 // TODO: Should we remove the shadow account so that it doesn't keep trying? 1284 } 1285 1286 }.bind(); 1287 } finally { 1288 restoreCallingIdentity(id); 1289 } 1290 } 1291 1292 private boolean addAccountInternal(UserAccounts accounts, Account account, String password, 1293 Bundle extras, int callingUid) { 1294 Bundle.setDefusable(extras, true); 1295 if (account == null) { 1296 return false; 1297 } 1298 if (!isLocalUnlockedUser(accounts.userId)) { 1299 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId 1300 + " is locked. callingUid=" + callingUid); 1301 return false; 1302 } 1303 synchronized (accounts.cacheLock) { 1304 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1305 db.beginTransaction(); 1306 try { 1307 long numMatches = DatabaseUtils.longForQuery(db, 1308 "select count(*) from " + CE_TABLE_ACCOUNTS 1309 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 1310 new String[]{account.name, account.type}); 1311 if (numMatches > 0) { 1312 Log.w(TAG, "insertAccountIntoDatabase: " + account 1313 + ", skipping since the account already exists"); 1314 return false; 1315 } 1316 ContentValues values = new ContentValues(); 1317 values.put(ACCOUNTS_NAME, account.name); 1318 values.put(ACCOUNTS_TYPE, account.type); 1319 values.put(ACCOUNTS_PASSWORD, password); 1320 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values); 1321 if (accountId < 0) { 1322 Log.w(TAG, "insertAccountIntoDatabase: " + account 1323 + ", skipping the DB insert failed"); 1324 return false; 1325 } 1326 // Insert into DE table 1327 values = new ContentValues(); 1328 values.put(ACCOUNTS_ID, accountId); 1329 values.put(ACCOUNTS_NAME, account.name); 1330 values.put(ACCOUNTS_TYPE, account.type); 1331 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, 1332 System.currentTimeMillis()); 1333 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) { 1334 Log.w(TAG, "insertAccountIntoDatabase: " + account 1335 + ", skipping the DB insert failed"); 1336 return false; 1337 } 1338 if (extras != null) { 1339 for (String key : extras.keySet()) { 1340 final String value = extras.getString(key); 1341 if (insertExtraLocked(db, accountId, key, value) < 0) { 1342 Log.w(TAG, "insertAccountIntoDatabase: " + account 1343 + ", skipping since insertExtra failed for key " + key); 1344 return false; 1345 } 1346 } 1347 } 1348 db.setTransactionSuccessful(); 1349 1350 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId, 1351 accounts, callingUid); 1352 1353 insertAccountIntoCacheLocked(accounts, account); 1354 } finally { 1355 db.endTransaction(); 1356 } 1357 } 1358 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) { 1359 addAccountToLinkedRestrictedUsers(account, accounts.userId); 1360 } 1361 1362 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed. 1363 sendAccountsChangedBroadcast(accounts.userId); 1364 return true; 1365 } 1366 1367 private boolean isLocalUnlockedUser(int userId) { 1368 synchronized (mUsers) { 1369 return mLocalUnlockedUsers.get(userId); 1370 } 1371 } 1372 1373 /** 1374 * Adds the account to all linked restricted users as shared accounts. If the user is currently 1375 * running, then clone the account too. 1376 * @param account the account to share with limited users 1377 * 1378 */ 1379 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) { 1380 List<UserInfo> users = getUserManager().getUsers(); 1381 for (UserInfo user : users) { 1382 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) { 1383 addSharedAccountAsUser(account, user.id); 1384 if (isLocalUnlockedUser(user.id)) { 1385 mMessageHandler.sendMessage(mMessageHandler.obtainMessage( 1386 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account)); 1387 } 1388 } 1389 } 1390 } 1391 1392 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { 1393 ContentValues values = new ContentValues(); 1394 values.put(EXTRAS_KEY, key); 1395 values.put(EXTRAS_ACCOUNTS_ID, accountId); 1396 values.put(EXTRAS_VALUE, value); 1397 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values); 1398 } 1399 1400 @Override 1401 public void hasFeatures(IAccountManagerResponse response, 1402 Account account, String[] features, String opPackageName) { 1403 int callingUid = Binder.getCallingUid(); 1404 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1405 Log.v(TAG, "hasFeatures: " + account 1406 + ", response " + response 1407 + ", features " + stringArrayToString(features) 1408 + ", caller's uid " + callingUid 1409 + ", pid " + Binder.getCallingPid()); 1410 } 1411 if (response == null) throw new IllegalArgumentException("response is null"); 1412 if (account == null) throw new IllegalArgumentException("account is null"); 1413 if (features == null) throw new IllegalArgumentException("features is null"); 1414 int userId = UserHandle.getCallingUserId(); 1415 checkReadAccountsPermitted(callingUid, account.type, userId, 1416 opPackageName); 1417 1418 long identityToken = clearCallingIdentity(); 1419 try { 1420 UserAccounts accounts = getUserAccounts(userId); 1421 new TestFeaturesSession(accounts, response, account, features).bind(); 1422 } finally { 1423 restoreCallingIdentity(identityToken); 1424 } 1425 } 1426 1427 private class TestFeaturesSession extends Session { 1428 private final String[] mFeatures; 1429 private final Account mAccount; 1430 1431 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, 1432 Account account, String[] features) { 1433 super(accounts, response, account.type, false /* expectActivityLaunch */, 1434 true /* stripAuthTokenFromResult */, account.name, 1435 false /* authDetailsRequired */); 1436 mFeatures = features; 1437 mAccount = account; 1438 } 1439 1440 @Override 1441 public void run() throws RemoteException { 1442 try { 1443 mAuthenticator.hasFeatures(this, mAccount, mFeatures); 1444 } catch (RemoteException e) { 1445 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 1446 } 1447 } 1448 1449 @Override 1450 public void onResult(Bundle result) { 1451 Bundle.setDefusable(result, true); 1452 IAccountManagerResponse response = getResponseAndClose(); 1453 if (response != null) { 1454 try { 1455 if (result == null) { 1456 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 1457 return; 1458 } 1459 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1460 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1461 + response); 1462 } 1463 final Bundle newResult = new Bundle(); 1464 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 1465 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 1466 response.onResult(newResult); 1467 } catch (RemoteException e) { 1468 // if the caller is dead then there is no one to care about remote exceptions 1469 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1470 Log.v(TAG, "failure while notifying response", e); 1471 } 1472 } 1473 } 1474 } 1475 1476 @Override 1477 protected String toDebugString(long now) { 1478 return super.toDebugString(now) + ", hasFeatures" 1479 + ", " + mAccount 1480 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 1481 } 1482 } 1483 1484 @Override 1485 public void renameAccount( 1486 IAccountManagerResponse response, Account accountToRename, String newName) { 1487 final int callingUid = Binder.getCallingUid(); 1488 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1489 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName 1490 + ", caller's uid " + callingUid 1491 + ", pid " + Binder.getCallingPid()); 1492 } 1493 if (accountToRename == null) throw new IllegalArgumentException("account is null"); 1494 int userId = UserHandle.getCallingUserId(); 1495 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { 1496 String msg = String.format( 1497 "uid %s cannot rename accounts of type: %s", 1498 callingUid, 1499 accountToRename.type); 1500 throw new SecurityException(msg); 1501 } 1502 long identityToken = clearCallingIdentity(); 1503 try { 1504 UserAccounts accounts = getUserAccounts(userId); 1505 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName); 1506 Bundle result = new Bundle(); 1507 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); 1508 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); 1509 try { 1510 response.onResult(result); 1511 } catch (RemoteException e) { 1512 Log.w(TAG, e.getMessage()); 1513 } 1514 } finally { 1515 restoreCallingIdentity(identityToken); 1516 } 1517 } 1518 1519 private Account renameAccountInternal( 1520 UserAccounts accounts, Account accountToRename, String newName) { 1521 Account resultAccount = null; 1522 /* 1523 * Cancel existing notifications. Let authenticators 1524 * re-post notifications as required. But we don't know if 1525 * the authenticators have bound their notifications to 1526 * now stale account name data. 1527 * 1528 * With a rename api, we might not need to do this anymore but it 1529 * shouldn't hurt. 1530 */ 1531 cancelNotification( 1532 getSigninRequiredNotificationId(accounts, accountToRename), 1533 new UserHandle(accounts.userId)); 1534 synchronized(accounts.credentialsPermissionNotificationIds) { 1535 for (Pair<Pair<Account, String>, Integer> pair: 1536 accounts.credentialsPermissionNotificationIds.keySet()) { 1537 if (accountToRename.equals(pair.first.first)) { 1538 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1539 cancelNotification(id, new UserHandle(accounts.userId)); 1540 } 1541 } 1542 } 1543 synchronized (accounts.cacheLock) { 1544 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1545 db.beginTransaction(); 1546 Account renamedAccount = new Account(newName, accountToRename.type); 1547 try { 1548 final long accountId = getAccountIdLocked(db, accountToRename); 1549 if (accountId >= 0) { 1550 final ContentValues values = new ContentValues(); 1551 values.put(ACCOUNTS_NAME, newName); 1552 final String[] argsAccountId = { String.valueOf(accountId) }; 1553 db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 1554 // Update NAME/PREVIOUS_NAME in DE accounts table 1555 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name); 1556 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 1557 db.setTransactionSuccessful(); 1558 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId, 1559 accounts); 1560 } 1561 } finally { 1562 db.endTransaction(); 1563 } 1564 /* 1565 * Database transaction was successful. Clean up cached 1566 * data associated with the account in the user profile. 1567 */ 1568 insertAccountIntoCacheLocked(accounts, renamedAccount); 1569 /* 1570 * Extract the data and token caches before removing the 1571 * old account to preserve the user data associated with 1572 * the account. 1573 */ 1574 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename); 1575 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename); 1576 removeAccountFromCacheLocked(accounts, accountToRename); 1577 /* 1578 * Update the cached data associated with the renamed 1579 * account. 1580 */ 1581 accounts.userDataCache.put(renamedAccount, tmpData); 1582 accounts.authTokenCache.put(renamedAccount, tmpTokens); 1583 accounts.previousNameCache.put( 1584 renamedAccount, 1585 new AtomicReference<String>(accountToRename.name)); 1586 resultAccount = renamedAccount; 1587 1588 int parentUserId = accounts.userId; 1589 if (canHaveProfile(parentUserId)) { 1590 /* 1591 * Owner or system user account was renamed, rename the account for 1592 * those users with which the account was shared. 1593 */ 1594 List<UserInfo> users = getUserManager().getUsers(true); 1595 for (UserInfo user : users) { 1596 if (user.isRestricted() 1597 && (user.restrictedProfileParentId == parentUserId)) { 1598 renameSharedAccountAsUser(accountToRename, newName, user.id); 1599 } 1600 } 1601 } 1602 sendAccountsChangedBroadcast(accounts.userId); 1603 } 1604 return resultAccount; 1605 } 1606 1607 private boolean canHaveProfile(final int parentUserId) { 1608 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId); 1609 return userInfo != null && userInfo.canHaveProfile(); 1610 } 1611 1612 @Override 1613 public void removeAccount(IAccountManagerResponse response, Account account, 1614 boolean expectActivityLaunch) { 1615 removeAccountAsUser( 1616 response, 1617 account, 1618 expectActivityLaunch, 1619 UserHandle.getCallingUserId()); 1620 } 1621 1622 @Override 1623 public void removeAccountAsUser(IAccountManagerResponse response, Account account, 1624 boolean expectActivityLaunch, int userId) { 1625 final int callingUid = Binder.getCallingUid(); 1626 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1627 Log.v(TAG, "removeAccount: " + account 1628 + ", response " + response 1629 + ", caller's uid " + callingUid 1630 + ", pid " + Binder.getCallingPid() 1631 + ", for user id " + userId); 1632 } 1633 if (response == null) throw new IllegalArgumentException("response is null"); 1634 if (account == null) throw new IllegalArgumentException("account is null"); 1635 // Only allow the system process to modify accounts of other users 1636 if (isCrossUser(callingUid, userId)) { 1637 throw new SecurityException( 1638 String.format( 1639 "User %s tying remove account for %s" , 1640 UserHandle.getCallingUserId(), 1641 userId)); 1642 } 1643 /* 1644 * Only the system or authenticator should be allowed to remove accounts for that 1645 * authenticator. This will let users remove accounts (via Settings in the system) but not 1646 * arbitrary applications (like competing authenticators). 1647 */ 1648 UserHandle user = UserHandle.of(userId); 1649 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) 1650 && !isSystemUid(callingUid)) { 1651 String msg = String.format( 1652 "uid %s cannot remove accounts of type: %s", 1653 callingUid, 1654 account.type); 1655 throw new SecurityException(msg); 1656 } 1657 if (!canUserModifyAccounts(userId, callingUid)) { 1658 try { 1659 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1660 "User cannot modify accounts"); 1661 } catch (RemoteException re) { 1662 } 1663 return; 1664 } 1665 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) { 1666 try { 1667 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1668 "User cannot modify accounts of this type (policy)."); 1669 } catch (RemoteException re) { 1670 } 1671 return; 1672 } 1673 long identityToken = clearCallingIdentity(); 1674 UserAccounts accounts = getUserAccounts(userId); 1675 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 1676 synchronized(accounts.credentialsPermissionNotificationIds) { 1677 for (Pair<Pair<Account, String>, Integer> pair: 1678 accounts.credentialsPermissionNotificationIds.keySet()) { 1679 if (account.equals(pair.first.first)) { 1680 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1681 cancelNotification(id, user); 1682 } 1683 } 1684 } 1685 SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 1686 final long accountId = getAccountIdLocked(db, account); 1687 logRecord( 1688 db, 1689 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, 1690 TABLE_ACCOUNTS, 1691 accountId, 1692 accounts, 1693 callingUid); 1694 try { 1695 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); 1696 } finally { 1697 restoreCallingIdentity(identityToken); 1698 } 1699 } 1700 1701 @Override 1702 public boolean removeAccountExplicitly(Account account) { 1703 final int callingUid = Binder.getCallingUid(); 1704 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1705 Log.v(TAG, "removeAccountExplicitly: " + account 1706 + ", caller's uid " + callingUid 1707 + ", pid " + Binder.getCallingPid()); 1708 } 1709 int userId = Binder.getCallingUserHandle().getIdentifier(); 1710 if (account == null) { 1711 /* 1712 * Null accounts should result in returning false, as per 1713 * AccountManage.addAccountExplicitly(...) java doc. 1714 */ 1715 Log.e(TAG, "account is null"); 1716 return false; 1717 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1718 String msg = String.format( 1719 "uid %s cannot explicitly add accounts of type: %s", 1720 callingUid, 1721 account.type); 1722 throw new SecurityException(msg); 1723 } 1724 UserAccounts accounts = getUserAccountsForCaller(); 1725 SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 1726 final long accountId = getAccountIdLocked(db, account); 1727 logRecord( 1728 db, 1729 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, 1730 TABLE_ACCOUNTS, 1731 accountId, 1732 accounts, 1733 callingUid); 1734 long identityToken = clearCallingIdentity(); 1735 try { 1736 return removeAccountInternal(accounts, account, callingUid); 1737 } finally { 1738 restoreCallingIdentity(identityToken); 1739 } 1740 } 1741 1742 private class RemoveAccountSession extends Session { 1743 final Account mAccount; 1744 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, 1745 Account account, boolean expectActivityLaunch) { 1746 super(accounts, response, account.type, expectActivityLaunch, 1747 true /* stripAuthTokenFromResult */, account.name, 1748 false /* authDetailsRequired */); 1749 mAccount = account; 1750 } 1751 1752 @Override 1753 protected String toDebugString(long now) { 1754 return super.toDebugString(now) + ", removeAccount" 1755 + ", account " + mAccount; 1756 } 1757 1758 @Override 1759 public void run() throws RemoteException { 1760 mAuthenticator.getAccountRemovalAllowed(this, mAccount); 1761 } 1762 1763 @Override 1764 public void onResult(Bundle result) { 1765 Bundle.setDefusable(result, true); 1766 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) 1767 && !result.containsKey(AccountManager.KEY_INTENT)) { 1768 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); 1769 if (removalAllowed) { 1770 removeAccountInternal(mAccounts, mAccount, getCallingUid()); 1771 } 1772 IAccountManagerResponse response = getResponseAndClose(); 1773 if (response != null) { 1774 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1775 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1776 + response); 1777 } 1778 Bundle result2 = new Bundle(); 1779 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); 1780 try { 1781 response.onResult(result2); 1782 } catch (RemoteException e) { 1783 // ignore 1784 } 1785 } 1786 } 1787 super.onResult(result); 1788 } 1789 } 1790 1791 @VisibleForTesting 1792 protected void removeAccountInternal(Account account) { 1793 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid()); 1794 } 1795 1796 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) { 1797 boolean isChanged = false; 1798 boolean userUnlocked = isLocalUnlockedUser(accounts.userId); 1799 if (!userUnlocked) { 1800 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId 1801 + " is still locked. CE data will be removed later"); 1802 } 1803 synchronized (accounts.cacheLock) { 1804 final SQLiteDatabase db = userUnlocked 1805 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked() 1806 : accounts.openHelper.getWritableDatabase(); 1807 db.beginTransaction(); 1808 // Set to a dummy value, this will only be used if the database 1809 // transaction succeeds. 1810 long accountId = -1; 1811 try { 1812 accountId = getAccountIdLocked(db, account); 1813 if (accountId >= 0) { 1814 db.delete( 1815 TABLE_ACCOUNTS, 1816 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 1817 new String[]{ account.name, account.type }); 1818 if (userUnlocked) { 1819 // Delete from CE table 1820 db.delete( 1821 CE_TABLE_ACCOUNTS, 1822 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 1823 new String[]{ account.name, account.type }); 1824 } 1825 db.setTransactionSuccessful(); 1826 isChanged = true; 1827 } 1828 } finally { 1829 db.endTransaction(); 1830 } 1831 if (isChanged) { 1832 removeAccountFromCacheLocked(accounts, account); 1833 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured. 1834 sendAccountsChangedBroadcast(accounts.userId); 1835 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE 1836 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE; 1837 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts); 1838 } 1839 } 1840 long id = Binder.clearCallingIdentity(); 1841 try { 1842 int parentUserId = accounts.userId; 1843 if (canHaveProfile(parentUserId)) { 1844 // Remove from any restricted profiles that are sharing this account. 1845 List<UserInfo> users = getUserManager().getUsers(true); 1846 for (UserInfo user : users) { 1847 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) { 1848 removeSharedAccountAsUser(account, user.id, callingUid); 1849 } 1850 } 1851 } 1852 } finally { 1853 Binder.restoreCallingIdentity(id); 1854 } 1855 1856 if (isChanged) { 1857 synchronized (accounts.credentialsPermissionNotificationIds) { 1858 for (Pair<Pair<Account, String>, Integer> key 1859 : accounts.credentialsPermissionNotificationIds.keySet()) { 1860 if (account.equals(key.first.first) 1861 && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) { 1862 final int uid = (Integer) key.second; 1863 mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( 1864 account, uid, false)); 1865 } 1866 } 1867 } 1868 } 1869 1870 return isChanged; 1871 } 1872 1873 @Override 1874 public void invalidateAuthToken(String accountType, String authToken) { 1875 int callerUid = Binder.getCallingUid(); 1876 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1877 Log.v(TAG, "invalidateAuthToken: accountType " + accountType 1878 + ", caller's uid " + callerUid 1879 + ", pid " + Binder.getCallingPid()); 1880 } 1881 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1882 if (authToken == null) throw new IllegalArgumentException("authToken is null"); 1883 int userId = UserHandle.getCallingUserId(); 1884 long identityToken = clearCallingIdentity(); 1885 try { 1886 UserAccounts accounts = getUserAccounts(userId); 1887 synchronized (accounts.cacheLock) { 1888 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1889 db.beginTransaction(); 1890 try { 1891 invalidateAuthTokenLocked(accounts, db, accountType, authToken); 1892 invalidateCustomTokenLocked(accounts, accountType, authToken); 1893 db.setTransactionSuccessful(); 1894 } finally { 1895 db.endTransaction(); 1896 } 1897 } 1898 } finally { 1899 restoreCallingIdentity(identityToken); 1900 } 1901 } 1902 1903 private void invalidateCustomTokenLocked( 1904 UserAccounts accounts, 1905 String accountType, 1906 String authToken) { 1907 if (authToken == null || accountType == null) { 1908 return; 1909 } 1910 // Also wipe out cached token in memory. 1911 accounts.accountTokenCaches.remove(accountType, authToken); 1912 } 1913 1914 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, 1915 String accountType, String authToken) { 1916 if (authToken == null || accountType == null) { 1917 return; 1918 } 1919 Cursor cursor = db.rawQuery( 1920 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID 1921 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 1922 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE 1923 + " FROM " + CE_TABLE_ACCOUNTS 1924 + " JOIN " + CE_TABLE_AUTHTOKENS 1925 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1926 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID 1927 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN 1928 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", 1929 new String[]{authToken, accountType}); 1930 try { 1931 while (cursor.moveToNext()) { 1932 long authTokenId = cursor.getLong(0); 1933 String accountName = cursor.getString(1); 1934 String authTokenType = cursor.getString(2); 1935 db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); 1936 writeAuthTokenIntoCacheLocked( 1937 accounts, 1938 db, 1939 new Account(accountName, accountType), 1940 authTokenType, 1941 null); 1942 } 1943 } finally { 1944 cursor.close(); 1945 } 1946 } 1947 1948 private void saveCachedToken( 1949 UserAccounts accounts, 1950 Account account, 1951 String callerPkg, 1952 byte[] callerSigDigest, 1953 String tokenType, 1954 String token, 1955 long expiryMillis) { 1956 1957 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) { 1958 return; 1959 } 1960 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1961 UserHandle.of(accounts.userId)); 1962 synchronized (accounts.cacheLock) { 1963 accounts.accountTokenCaches.put( 1964 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis); 1965 } 1966 } 1967 1968 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, 1969 String authToken) { 1970 if (account == null || type == null) { 1971 return false; 1972 } 1973 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1974 UserHandle.of(accounts.userId)); 1975 synchronized (accounts.cacheLock) { 1976 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1977 db.beginTransaction(); 1978 try { 1979 long accountId = getAccountIdLocked(db, account); 1980 if (accountId < 0) { 1981 return false; 1982 } 1983 db.delete(CE_TABLE_AUTHTOKENS, 1984 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", 1985 new String[]{type}); 1986 ContentValues values = new ContentValues(); 1987 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); 1988 values.put(AUTHTOKENS_TYPE, type); 1989 values.put(AUTHTOKENS_AUTHTOKEN, authToken); 1990 if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { 1991 db.setTransactionSuccessful(); 1992 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); 1993 return true; 1994 } 1995 return false; 1996 } finally { 1997 db.endTransaction(); 1998 } 1999 } 2000 } 2001 2002 @Override 2003 public String peekAuthToken(Account account, String authTokenType) { 2004 final int callingUid = Binder.getCallingUid(); 2005 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2006 Log.v(TAG, "peekAuthToken: " + account 2007 + ", authTokenType " + authTokenType 2008 + ", caller's uid " + callingUid 2009 + ", pid " + Binder.getCallingPid()); 2010 } 2011 if (account == null) throw new IllegalArgumentException("account is null"); 2012 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2013 int userId = UserHandle.getCallingUserId(); 2014 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2015 String msg = String.format( 2016 "uid %s cannot peek the authtokens associated with accounts of type: %s", 2017 callingUid, 2018 account.type); 2019 throw new SecurityException(msg); 2020 } 2021 if (!isLocalUnlockedUser(userId)) { 2022 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid " 2023 + callingUid); 2024 return null; 2025 } 2026 long identityToken = clearCallingIdentity(); 2027 try { 2028 UserAccounts accounts = getUserAccounts(userId); 2029 return readAuthTokenInternal(accounts, account, authTokenType); 2030 } finally { 2031 restoreCallingIdentity(identityToken); 2032 } 2033 } 2034 2035 @Override 2036 public void setAuthToken(Account account, String authTokenType, String authToken) { 2037 final int callingUid = Binder.getCallingUid(); 2038 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2039 Log.v(TAG, "setAuthToken: " + account 2040 + ", authTokenType " + authTokenType 2041 + ", caller's uid " + callingUid 2042 + ", pid " + Binder.getCallingPid()); 2043 } 2044 if (account == null) throw new IllegalArgumentException("account is null"); 2045 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2046 int userId = UserHandle.getCallingUserId(); 2047 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2048 String msg = String.format( 2049 "uid %s cannot set auth tokens associated with accounts of type: %s", 2050 callingUid, 2051 account.type); 2052 throw new SecurityException(msg); 2053 } 2054 long identityToken = clearCallingIdentity(); 2055 try { 2056 UserAccounts accounts = getUserAccounts(userId); 2057 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); 2058 } finally { 2059 restoreCallingIdentity(identityToken); 2060 } 2061 } 2062 2063 @Override 2064 public void setPassword(Account account, String password) { 2065 final int callingUid = Binder.getCallingUid(); 2066 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2067 Log.v(TAG, "setAuthToken: " + account 2068 + ", caller's uid " + callingUid 2069 + ", pid " + Binder.getCallingPid()); 2070 } 2071 if (account == null) throw new IllegalArgumentException("account is null"); 2072 int userId = UserHandle.getCallingUserId(); 2073 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2074 String msg = String.format( 2075 "uid %s cannot set secrets for accounts of type: %s", 2076 callingUid, 2077 account.type); 2078 throw new SecurityException(msg); 2079 } 2080 long identityToken = clearCallingIdentity(); 2081 try { 2082 UserAccounts accounts = getUserAccounts(userId); 2083 setPasswordInternal(accounts, account, password, callingUid); 2084 } finally { 2085 restoreCallingIdentity(identityToken); 2086 } 2087 } 2088 2089 private void setPasswordInternal(UserAccounts accounts, Account account, String password, 2090 int callingUid) { 2091 if (account == null) { 2092 return; 2093 } 2094 boolean isChanged = false; 2095 synchronized (accounts.cacheLock) { 2096 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 2097 db.beginTransaction(); 2098 try { 2099 final ContentValues values = new ContentValues(); 2100 values.put(ACCOUNTS_PASSWORD, password); 2101 final long accountId = getAccountIdLocked(db, account); 2102 if (accountId >= 0) { 2103 final String[] argsAccountId = {String.valueOf(accountId)}; 2104 db.update( 2105 CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 2106 db.delete( 2107 CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); 2108 accounts.authTokenCache.remove(account); 2109 accounts.accountTokenCaches.remove(account); 2110 db.setTransactionSuccessful(); 2111 // If there is an account whose password will be updated and the database 2112 // transactions succeed, then we say that a change has occured. Even if the 2113 // new password is the same as the old and there were no authtokens to delete. 2114 isChanged = true; 2115 String action = (password == null || password.length() == 0) ? 2116 DebugDbHelper.ACTION_CLEAR_PASSWORD 2117 : DebugDbHelper.ACTION_SET_PASSWORD; 2118 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid); 2119 } 2120 } finally { 2121 db.endTransaction(); 2122 if (isChanged) { 2123 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed. 2124 sendAccountsChangedBroadcast(accounts.userId); 2125 } 2126 } 2127 } 2128 } 2129 2130 private void sendAccountsChangedBroadcast(int userId) { 2131 Log.i(TAG, "the accounts changed, sending broadcast of " 2132 + ACCOUNTS_CHANGED_INTENT.getAction()); 2133 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); 2134 } 2135 2136 @Override 2137 public void clearPassword(Account account) { 2138 final int callingUid = Binder.getCallingUid(); 2139 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2140 Log.v(TAG, "clearPassword: " + account 2141 + ", caller's uid " + callingUid 2142 + ", pid " + Binder.getCallingPid()); 2143 } 2144 if (account == null) throw new IllegalArgumentException("account is null"); 2145 int userId = UserHandle.getCallingUserId(); 2146 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2147 String msg = String.format( 2148 "uid %s cannot clear passwords for accounts of type: %s", 2149 callingUid, 2150 account.type); 2151 throw new SecurityException(msg); 2152 } 2153 long identityToken = clearCallingIdentity(); 2154 try { 2155 UserAccounts accounts = getUserAccounts(userId); 2156 setPasswordInternal(accounts, account, null, callingUid); 2157 } finally { 2158 restoreCallingIdentity(identityToken); 2159 } 2160 } 2161 2162 @Override 2163 public void setUserData(Account account, String key, String value) { 2164 final int callingUid = Binder.getCallingUid(); 2165 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2166 Log.v(TAG, "setUserData: " + account 2167 + ", key " + key 2168 + ", caller's uid " + callingUid 2169 + ", pid " + Binder.getCallingPid()); 2170 } 2171 if (key == null) throw new IllegalArgumentException("key is null"); 2172 if (account == null) throw new IllegalArgumentException("account is null"); 2173 int userId = UserHandle.getCallingUserId(); 2174 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2175 String msg = String.format( 2176 "uid %s cannot set user data for accounts of type: %s", 2177 callingUid, 2178 account.type); 2179 throw new SecurityException(msg); 2180 } 2181 long identityToken = clearCallingIdentity(); 2182 try { 2183 UserAccounts accounts = getUserAccounts(userId); 2184 synchronized (accounts.cacheLock) { 2185 if (!accountExistsCacheLocked(accounts, account)) { 2186 return; 2187 } 2188 setUserdataInternalLocked(accounts, account, key, value); 2189 } 2190 } finally { 2191 restoreCallingIdentity(identityToken); 2192 } 2193 } 2194 2195 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) { 2196 if (accounts.accountCache.containsKey(account.type)) { 2197 for (Account acc : accounts.accountCache.get(account.type)) { 2198 if (acc.name.equals(account.name)) { 2199 return true; 2200 } 2201 } 2202 } 2203 return false; 2204 } 2205 2206 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key, 2207 String value) { 2208 if (account == null || key == null) { 2209 return; 2210 } 2211 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2212 db.beginTransaction(); 2213 try { 2214 long accountId = getAccountIdLocked(db, account); 2215 if (accountId < 0) { 2216 return; 2217 } 2218 long extrasId = getExtrasIdLocked(db, accountId, key); 2219 if (extrasId < 0) { 2220 extrasId = insertExtraLocked(db, accountId, key, value); 2221 if (extrasId < 0) { 2222 return; 2223 } 2224 } else { 2225 ContentValues values = new ContentValues(); 2226 values.put(EXTRAS_VALUE, value); 2227 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { 2228 return; 2229 } 2230 } 2231 writeUserDataIntoCacheLocked(accounts, db, account, key, value); 2232 db.setTransactionSuccessful(); 2233 } finally { 2234 db.endTransaction(); 2235 } 2236 } 2237 2238 private void onResult(IAccountManagerResponse response, Bundle result) { 2239 if (result == null) { 2240 Log.e(TAG, "the result is unexpectedly null", new Exception()); 2241 } 2242 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2243 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2244 + response); 2245 } 2246 try { 2247 response.onResult(result); 2248 } catch (RemoteException e) { 2249 // if the caller is dead then there is no one to care about remote 2250 // exceptions 2251 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2252 Log.v(TAG, "failure while notifying response", e); 2253 } 2254 } 2255 } 2256 2257 @Override 2258 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, 2259 final String authTokenType) 2260 throws RemoteException { 2261 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2262 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2263 2264 final int callingUid = getCallingUid(); 2265 clearCallingIdentity(); 2266 if (callingUid != Process.SYSTEM_UID) { 2267 throw new SecurityException("can only call from system"); 2268 } 2269 int userId = UserHandle.getUserId(callingUid); 2270 long identityToken = clearCallingIdentity(); 2271 try { 2272 UserAccounts accounts = getUserAccounts(userId); 2273 new Session(accounts, response, accountType, false /* expectActivityLaunch */, 2274 false /* stripAuthTokenFromResult */, null /* accountName */, 2275 false /* authDetailsRequired */) { 2276 @Override 2277 protected String toDebugString(long now) { 2278 return super.toDebugString(now) + ", getAuthTokenLabel" 2279 + ", " + accountType 2280 + ", authTokenType " + authTokenType; 2281 } 2282 2283 @Override 2284 public void run() throws RemoteException { 2285 mAuthenticator.getAuthTokenLabel(this, authTokenType); 2286 } 2287 2288 @Override 2289 public void onResult(Bundle result) { 2290 Bundle.setDefusable(result, true); 2291 if (result != null) { 2292 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); 2293 Bundle bundle = new Bundle(); 2294 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); 2295 super.onResult(bundle); 2296 return; 2297 } else { 2298 super.onResult(result); 2299 } 2300 } 2301 }.bind(); 2302 } finally { 2303 restoreCallingIdentity(identityToken); 2304 } 2305 } 2306 2307 @Override 2308 public void getAuthToken( 2309 IAccountManagerResponse response, 2310 final Account account, 2311 final String authTokenType, 2312 final boolean notifyOnAuthFailure, 2313 final boolean expectActivityLaunch, 2314 final Bundle loginOptions) { 2315 Bundle.setDefusable(loginOptions, true); 2316 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2317 Log.v(TAG, "getAuthToken: " + account 2318 + ", response " + response 2319 + ", authTokenType " + authTokenType 2320 + ", notifyOnAuthFailure " + notifyOnAuthFailure 2321 + ", expectActivityLaunch " + expectActivityLaunch 2322 + ", caller's uid " + Binder.getCallingUid() 2323 + ", pid " + Binder.getCallingPid()); 2324 } 2325 if (response == null) throw new IllegalArgumentException("response is null"); 2326 try { 2327 if (account == null) { 2328 Slog.w(TAG, "getAuthToken called with null account"); 2329 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null"); 2330 return; 2331 } 2332 if (authTokenType == null) { 2333 Slog.w(TAG, "getAuthToken called with null authTokenType"); 2334 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null"); 2335 return; 2336 } 2337 } catch (RemoteException e) { 2338 Slog.w(TAG, "Failed to report error back to the client." + e); 2339 return; 2340 } 2341 int userId = UserHandle.getCallingUserId(); 2342 long ident = Binder.clearCallingIdentity(); 2343 final UserAccounts accounts; 2344 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 2345 try { 2346 accounts = getUserAccounts(userId); 2347 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 2348 AuthenticatorDescription.newKey(account.type), accounts.userId); 2349 } finally { 2350 Binder.restoreCallingIdentity(ident); 2351 } 2352 2353 final boolean customTokens = 2354 authenticatorInfo != null && authenticatorInfo.type.customTokens; 2355 2356 // skip the check if customTokens 2357 final int callerUid = Binder.getCallingUid(); 2358 final boolean permissionGranted = 2359 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); 2360 2361 // Get the calling package. We will use it for the purpose of caching. 2362 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 2363 List<String> callerOwnedPackageNames; 2364 ident = Binder.clearCallingIdentity(); 2365 try { 2366 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid)); 2367 } finally { 2368 Binder.restoreCallingIdentity(ident); 2369 } 2370 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) { 2371 String msg = String.format( 2372 "Uid %s is attempting to illegally masquerade as package %s!", 2373 callerUid, 2374 callerPkg); 2375 throw new SecurityException(msg); 2376 } 2377 2378 // let authenticator know the identity of the caller 2379 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); 2380 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); 2381 2382 if (notifyOnAuthFailure) { 2383 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); 2384 } 2385 2386 long identityToken = clearCallingIdentity(); 2387 try { 2388 // Distill the caller's package signatures into a single digest. 2389 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg); 2390 2391 // if the caller has permission, do the peek. otherwise go the more expensive 2392 // route of starting a Session 2393 if (!customTokens && permissionGranted) { 2394 String authToken = readAuthTokenInternal(accounts, account, authTokenType); 2395 if (authToken != null) { 2396 Bundle result = new Bundle(); 2397 result.putString(AccountManager.KEY_AUTHTOKEN, authToken); 2398 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2399 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2400 onResult(response, result); 2401 return; 2402 } 2403 } 2404 2405 if (customTokens) { 2406 /* 2407 * Look up tokens in the new cache only if the loginOptions don't have parameters 2408 * outside of those expected to be injected by the AccountManager, e.g. 2409 * ANDORID_PACKAGE_NAME. 2410 */ 2411 String token = readCachedTokenInternal( 2412 accounts, 2413 account, 2414 authTokenType, 2415 callerPkg, 2416 callerPkgSigDigest); 2417 if (token != null) { 2418 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2419 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); 2420 } 2421 Bundle result = new Bundle(); 2422 result.putString(AccountManager.KEY_AUTHTOKEN, token); 2423 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2424 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2425 onResult(response, result); 2426 return; 2427 } 2428 } 2429 2430 new Session( 2431 accounts, 2432 response, 2433 account.type, 2434 expectActivityLaunch, 2435 false /* stripAuthTokenFromResult */, 2436 account.name, 2437 false /* authDetailsRequired */) { 2438 @Override 2439 protected String toDebugString(long now) { 2440 if (loginOptions != null) loginOptions.keySet(); 2441 return super.toDebugString(now) + ", getAuthToken" 2442 + ", " + account 2443 + ", authTokenType " + authTokenType 2444 + ", loginOptions " + loginOptions 2445 + ", notifyOnAuthFailure " + notifyOnAuthFailure; 2446 } 2447 2448 @Override 2449 public void run() throws RemoteException { 2450 // If the caller doesn't have permission then create and return the 2451 // "grant permission" intent instead of the "getAuthToken" intent. 2452 if (!permissionGranted) { 2453 mAuthenticator.getAuthTokenLabel(this, authTokenType); 2454 } else { 2455 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); 2456 } 2457 } 2458 2459 @Override 2460 public void onResult(Bundle result) { 2461 Bundle.setDefusable(result, true); 2462 if (result != null) { 2463 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { 2464 Intent intent = newGrantCredentialsPermissionIntent( 2465 account, 2466 null, 2467 callerUid, 2468 new AccountAuthenticatorResponse(this), 2469 authTokenType, 2470 true); 2471 Bundle bundle = new Bundle(); 2472 bundle.putParcelable(AccountManager.KEY_INTENT, intent); 2473 onResult(bundle); 2474 return; 2475 } 2476 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 2477 if (authToken != null) { 2478 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); 2479 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 2480 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { 2481 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 2482 "the type and name should not be empty"); 2483 return; 2484 } 2485 Account resultAccount = new Account(name, type); 2486 if (!customTokens) { 2487 saveAuthTokenToDatabase( 2488 mAccounts, 2489 resultAccount, 2490 authTokenType, 2491 authToken); 2492 } 2493 long expiryMillis = result.getLong( 2494 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L); 2495 if (customTokens 2496 && expiryMillis > System.currentTimeMillis()) { 2497 saveCachedToken( 2498 mAccounts, 2499 account, 2500 callerPkg, 2501 callerPkgSigDigest, 2502 authTokenType, 2503 authToken, 2504 expiryMillis); 2505 } 2506 } 2507 2508 Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 2509 if (intent != null && notifyOnAuthFailure && !customTokens) { 2510 /* 2511 * Make sure that the supplied intent is owned by the authenticator 2512 * giving it to the system. Otherwise a malicious authenticator could 2513 * have users launching arbitrary activities by tricking users to 2514 * interact with malicious notifications. 2515 */ 2516 checkKeyIntent( 2517 Binder.getCallingUid(), 2518 intent); 2519 doNotification(mAccounts, 2520 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), 2521 intent, "android", accounts.userId); 2522 } 2523 } 2524 super.onResult(result); 2525 } 2526 }.bind(); 2527 } finally { 2528 restoreCallingIdentity(identityToken); 2529 } 2530 } 2531 2532 private byte[] calculatePackageSignatureDigest(String callerPkg) { 2533 MessageDigest digester; 2534 try { 2535 digester = MessageDigest.getInstance("SHA-256"); 2536 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 2537 callerPkg, PackageManager.GET_SIGNATURES); 2538 for (Signature sig : pkgInfo.signatures) { 2539 digester.update(sig.toByteArray()); 2540 } 2541 } catch (NoSuchAlgorithmException x) { 2542 Log.wtf(TAG, "SHA-256 should be available", x); 2543 digester = null; 2544 } catch (NameNotFoundException e) { 2545 Log.w(TAG, "Could not find packageinfo for: " + callerPkg); 2546 digester = null; 2547 } 2548 return (digester == null) ? null : digester.digest(); 2549 } 2550 2551 private void createNoCredentialsPermissionNotification(Account account, Intent intent, 2552 String packageName, int userId) { 2553 int uid = intent.getIntExtra( 2554 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); 2555 String authTokenType = intent.getStringExtra( 2556 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); 2557 final String titleAndSubtitle = 2558 mContext.getString(R.string.permission_request_notification_with_subtitle, 2559 account.name); 2560 final int index = titleAndSubtitle.indexOf('\n'); 2561 String title = titleAndSubtitle; 2562 String subtitle = ""; 2563 if (index > 0) { 2564 title = titleAndSubtitle.substring(0, index); 2565 subtitle = titleAndSubtitle.substring(index + 1); 2566 } 2567 UserHandle user = new UserHandle(userId); 2568 Context contextForUser = getContextForUser(user); 2569 Notification n = new Notification.Builder(contextForUser) 2570 .setSmallIcon(android.R.drawable.stat_sys_warning) 2571 .setWhen(0) 2572 .setColor(contextForUser.getColor( 2573 com.android.internal.R.color.system_notification_accent_color)) 2574 .setContentTitle(title) 2575 .setContentText(subtitle) 2576 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, 2577 PendingIntent.FLAG_CANCEL_CURRENT, null, user)) 2578 .build(); 2579 installNotification(getCredentialPermissionNotificationId( 2580 account, authTokenType, uid), n, packageName, user.getIdentifier()); 2581 } 2582 2583 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, 2584 int uid, AccountAuthenticatorResponse response, String authTokenType, 2585 boolean startInNewTask) { 2586 2587 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); 2588 2589 if (startInNewTask) { 2590 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. 2591 // Since it was set in Eclair+ we can't change it without breaking apps using 2592 // the intent from a non-Activity context. This is the default behavior. 2593 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2594 } 2595 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account, 2596 authTokenType, uid) + (packageName != null ? packageName : ""))); 2597 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); 2598 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); 2599 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); 2600 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); 2601 2602 return intent; 2603 } 2604 2605 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, 2606 int uid) { 2607 Integer id; 2608 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 2609 synchronized (accounts.credentialsPermissionNotificationIds) { 2610 final Pair<Pair<Account, String>, Integer> key = 2611 new Pair<Pair<Account, String>, Integer>( 2612 new Pair<Account, String>(account, authTokenType), uid); 2613 id = accounts.credentialsPermissionNotificationIds.get(key); 2614 if (id == null) { 2615 id = mNotificationIds.incrementAndGet(); 2616 accounts.credentialsPermissionNotificationIds.put(key, id); 2617 } 2618 } 2619 return id; 2620 } 2621 2622 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { 2623 Integer id; 2624 synchronized (accounts.signinRequiredNotificationIds) { 2625 id = accounts.signinRequiredNotificationIds.get(account); 2626 if (id == null) { 2627 id = mNotificationIds.incrementAndGet(); 2628 accounts.signinRequiredNotificationIds.put(account, id); 2629 } 2630 } 2631 return id; 2632 } 2633 2634 @Override 2635 public void addAccount(final IAccountManagerResponse response, final String accountType, 2636 final String authTokenType, final String[] requiredFeatures, 2637 final boolean expectActivityLaunch, final Bundle optionsIn) { 2638 Bundle.setDefusable(optionsIn, true); 2639 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2640 Log.v(TAG, "addAccount: accountType " + accountType 2641 + ", response " + response 2642 + ", authTokenType " + authTokenType 2643 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2644 + ", expectActivityLaunch " + expectActivityLaunch 2645 + ", caller's uid " + Binder.getCallingUid() 2646 + ", pid " + Binder.getCallingPid()); 2647 } 2648 if (response == null) throw new IllegalArgumentException("response is null"); 2649 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2650 2651 // Is user disallowed from modifying accounts? 2652 final int uid = Binder.getCallingUid(); 2653 final int userId = UserHandle.getUserId(uid); 2654 if (!canUserModifyAccounts(userId, uid)) { 2655 try { 2656 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2657 "User is not allowed to add an account!"); 2658 } catch (RemoteException re) { 2659 } 2660 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2661 return; 2662 } 2663 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 2664 try { 2665 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2666 "User cannot modify accounts of this type (policy)."); 2667 } catch (RemoteException re) { 2668 } 2669 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2670 userId); 2671 return; 2672 } 2673 2674 final int pid = Binder.getCallingPid(); 2675 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2676 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2677 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2678 2679 int usrId = UserHandle.getCallingUserId(); 2680 long identityToken = clearCallingIdentity(); 2681 try { 2682 UserAccounts accounts = getUserAccounts(usrId); 2683 logRecordWithUid( 2684 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid); 2685 new Session(accounts, response, accountType, expectActivityLaunch, 2686 true /* stripAuthTokenFromResult */, null /* accountName */, 2687 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2688 @Override 2689 public void run() throws RemoteException { 2690 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2691 options); 2692 } 2693 2694 @Override 2695 protected String toDebugString(long now) { 2696 return super.toDebugString(now) + ", addAccount" 2697 + ", accountType " + accountType 2698 + ", requiredFeatures " 2699 + (requiredFeatures != null 2700 ? TextUtils.join(",", requiredFeatures) 2701 : null); 2702 } 2703 }.bind(); 2704 } finally { 2705 restoreCallingIdentity(identityToken); 2706 } 2707 } 2708 2709 @Override 2710 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 2711 final String authTokenType, final String[] requiredFeatures, 2712 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 2713 Bundle.setDefusable(optionsIn, true); 2714 int callingUid = Binder.getCallingUid(); 2715 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2716 Log.v(TAG, "addAccount: accountType " + accountType 2717 + ", response " + response 2718 + ", authTokenType " + authTokenType 2719 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2720 + ", expectActivityLaunch " + expectActivityLaunch 2721 + ", caller's uid " + Binder.getCallingUid() 2722 + ", pid " + Binder.getCallingPid() 2723 + ", for user id " + userId); 2724 } 2725 if (response == null) throw new IllegalArgumentException("response is null"); 2726 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2727 // Only allow the system process to add accounts of other users 2728 if (isCrossUser(callingUid, userId)) { 2729 throw new SecurityException( 2730 String.format( 2731 "User %s trying to add account for %s" , 2732 UserHandle.getCallingUserId(), 2733 userId)); 2734 } 2735 2736 // Is user disallowed from modifying accounts? 2737 if (!canUserModifyAccounts(userId, callingUid)) { 2738 try { 2739 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2740 "User is not allowed to add an account!"); 2741 } catch (RemoteException re) { 2742 } 2743 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2744 return; 2745 } 2746 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 2747 try { 2748 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2749 "User cannot modify accounts of this type (policy)."); 2750 } catch (RemoteException re) { 2751 } 2752 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2753 userId); 2754 return; 2755 } 2756 2757 final int pid = Binder.getCallingPid(); 2758 final int uid = Binder.getCallingUid(); 2759 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2760 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2761 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2762 2763 long identityToken = clearCallingIdentity(); 2764 try { 2765 UserAccounts accounts = getUserAccounts(userId); 2766 logRecordWithUid( 2767 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId); 2768 new Session(accounts, response, accountType, expectActivityLaunch, 2769 true /* stripAuthTokenFromResult */, null /* accountName */, 2770 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2771 @Override 2772 public void run() throws RemoteException { 2773 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2774 options); 2775 } 2776 2777 @Override 2778 protected String toDebugString(long now) { 2779 return super.toDebugString(now) + ", addAccount" 2780 + ", accountType " + accountType 2781 + ", requiredFeatures " 2782 + (requiredFeatures != null 2783 ? TextUtils.join(",", requiredFeatures) 2784 : null); 2785 } 2786 }.bind(); 2787 } finally { 2788 restoreCallingIdentity(identityToken); 2789 } 2790 } 2791 2792 @Override 2793 public void startAddAccountSession( 2794 final IAccountManagerResponse response, 2795 final String accountType, 2796 final String authTokenType, 2797 final String[] requiredFeatures, 2798 final boolean expectActivityLaunch, 2799 final Bundle optionsIn) { 2800 Bundle.setDefusable(optionsIn, true); 2801 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2802 Log.v(TAG, 2803 "startAddAccountSession: accountType " + accountType 2804 + ", response " + response 2805 + ", authTokenType " + authTokenType 2806 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2807 + ", expectActivityLaunch " + expectActivityLaunch 2808 + ", caller's uid " + Binder.getCallingUid() 2809 + ", pid " + Binder.getCallingPid()); 2810 } 2811 if (response == null) { 2812 throw new IllegalArgumentException("response is null"); 2813 } 2814 if (accountType == null) { 2815 throw new IllegalArgumentException("accountType is null"); 2816 } 2817 2818 final int uid = Binder.getCallingUid(); 2819 // Only allow system to start session 2820 if (!isSystemUid(uid)) { 2821 String msg = String.format( 2822 "uid %s cannot stat add account session.", 2823 uid); 2824 throw new SecurityException(msg); 2825 } 2826 2827 final int userId = UserHandle.getUserId(uid); 2828 if (!canUserModifyAccounts(userId, uid)) { 2829 try { 2830 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2831 "User is not allowed to add an account!"); 2832 } catch (RemoteException re) { 2833 } 2834 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2835 return; 2836 } 2837 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 2838 try { 2839 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2840 "User cannot modify accounts of this type (policy)."); 2841 } catch (RemoteException re) { 2842 } 2843 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2844 userId); 2845 return; 2846 } 2847 final int pid = Binder.getCallingPid(); 2848 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2849 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2850 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2851 2852 // Check to see if the Password should be included to the caller. 2853 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 2854 boolean isPasswordForwardingAllowed = isPermitted( 2855 callerPkg, uid, Manifest.permission.GET_PASSWORD); 2856 2857 int usrId = UserHandle.getCallingUserId(); 2858 long identityToken = clearCallingIdentity(); 2859 try { 2860 UserAccounts accounts = getUserAccounts(usrId); 2861 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD, 2862 TABLE_ACCOUNTS, uid); 2863 new StartAccountSession( 2864 accounts, 2865 response, 2866 accountType, 2867 expectActivityLaunch, 2868 null /* accountName */, 2869 false /* authDetailsRequired */, 2870 true /* updateLastAuthenticationTime */, 2871 isPasswordForwardingAllowed) { 2872 @Override 2873 public void run() throws RemoteException { 2874 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType, 2875 requiredFeatures, options); 2876 } 2877 2878 @Override 2879 protected String toDebugString(long now) { 2880 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures); 2881 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType " 2882 + accountType + ", requiredFeatures " 2883 + (requiredFeatures != null ? requiredFeaturesStr : null); 2884 } 2885 }.bind(); 2886 } finally { 2887 restoreCallingIdentity(identityToken); 2888 } 2889 } 2890 2891 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */ 2892 private abstract class StartAccountSession extends Session { 2893 2894 private final boolean mIsPasswordForwardingAllowed; 2895 2896 public StartAccountSession( 2897 UserAccounts accounts, 2898 IAccountManagerResponse response, 2899 String accountType, 2900 boolean expectActivityLaunch, 2901 String accountName, 2902 boolean authDetailsRequired, 2903 boolean updateLastAuthenticationTime, 2904 boolean isPasswordForwardingAllowed) { 2905 super(accounts, response, accountType, expectActivityLaunch, 2906 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired, 2907 updateLastAuthenticationTime); 2908 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed; 2909 } 2910 2911 @Override 2912 public void onResult(Bundle result) { 2913 Bundle.setDefusable(result, true); 2914 mNumResults++; 2915 Intent intent = null; 2916 if (result != null 2917 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 2918 checkKeyIntent( 2919 Binder.getCallingUid(), 2920 intent); 2921 // Omit passwords if the caller isn't permitted to see them. 2922 if (!mIsPasswordForwardingAllowed) { 2923 result.remove(AccountManager.KEY_PASSWORD); 2924 } 2925 } 2926 IAccountManagerResponse response; 2927 if (mExpectActivityLaunch && result != null 2928 && result.containsKey(AccountManager.KEY_INTENT)) { 2929 response = mResponse; 2930 } else { 2931 response = getResponseAndClose(); 2932 } 2933 if (response == null) { 2934 return; 2935 } 2936 if (result == null) { 2937 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2938 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response " 2939 + response); 2940 } 2941 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 2942 "null bundle returned"); 2943 return; 2944 } 2945 2946 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) { 2947 // All AccountManager error codes are greater 2948 // than 0 2949 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE), 2950 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 2951 return; 2952 } 2953 2954 // Strip auth token from result. 2955 result.remove(AccountManager.KEY_AUTHTOKEN); 2956 2957 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2958 Log.v(TAG, 2959 getClass().getSimpleName() + " calling onResult() on response " + response); 2960 } 2961 2962 // Get the session bundle created by authenticator. The 2963 // bundle contains data necessary for finishing the session 2964 // later. The session bundle will be encrypted here and 2965 // decrypted later when trying to finish the session. 2966 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 2967 if (sessionBundle != null) { 2968 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 2969 if (TextUtils.isEmpty(accountType) 2970 || !mAccountType.equalsIgnoreCase(accountType)) { 2971 Log.w(TAG, "Account type in session bundle doesn't match request."); 2972 } 2973 // Add accountType info to session bundle. This will 2974 // override any value set by authenticator. 2975 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType); 2976 2977 // Encrypt session bundle before returning to caller. 2978 try { 2979 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 2980 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle); 2981 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle); 2982 } catch (GeneralSecurityException e) { 2983 if (Log.isLoggable(TAG, Log.DEBUG)) { 2984 Log.v(TAG, "Failed to encrypt session bundle!", e); 2985 } 2986 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 2987 "failed to encrypt session bundle"); 2988 return; 2989 } 2990 } 2991 2992 sendResponse(response, result); 2993 } 2994 } 2995 2996 @Override 2997 public void finishSessionAsUser(IAccountManagerResponse response, 2998 @NonNull Bundle sessionBundle, 2999 boolean expectActivityLaunch, 3000 Bundle appInfo, 3001 int userId) { 3002 Bundle.setDefusable(sessionBundle, true); 3003 int callingUid = Binder.getCallingUid(); 3004 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3005 Log.v(TAG, 3006 "finishSession: response "+ response 3007 + ", expectActivityLaunch " + expectActivityLaunch 3008 + ", caller's uid " + callingUid 3009 + ", caller's user id " + UserHandle.getCallingUserId() 3010 + ", pid " + Binder.getCallingPid() 3011 + ", for user id " + userId); 3012 } 3013 if (response == null) { 3014 throw new IllegalArgumentException("response is null"); 3015 } 3016 3017 // Session bundle is the encrypted bundle of the original bundle created by authenticator. 3018 // Account type is added to it before encryption. 3019 if (sessionBundle == null || sessionBundle.size() == 0) { 3020 throw new IllegalArgumentException("sessionBundle is empty"); 3021 } 3022 3023 // Only allow the system process to finish session for other users 3024 if (isCrossUser(callingUid, userId)) { 3025 throw new SecurityException( 3026 String.format( 3027 "User %s trying to finish session for %s without cross user permission", 3028 UserHandle.getCallingUserId(), 3029 userId)); 3030 } 3031 3032 // Only allow system to finish session 3033 if (!isSystemUid(callingUid)) { 3034 String msg = String.format( 3035 "uid %s cannot finish session because it's not system uid.", 3036 callingUid); 3037 throw new SecurityException(msg); 3038 } 3039 3040 if (!canUserModifyAccounts(userId, callingUid)) { 3041 sendErrorResponse(response, 3042 AccountManager.ERROR_CODE_USER_RESTRICTED, 3043 "User is not allowed to add an account!"); 3044 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3045 return; 3046 } 3047 3048 final int pid = Binder.getCallingPid(); 3049 final Bundle decryptedBundle; 3050 final String accountType; 3051 // First decrypt session bundle to get account type for checking permission. 3052 try { 3053 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 3054 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle); 3055 if (decryptedBundle == null) { 3056 sendErrorResponse( 3057 response, 3058 AccountManager.ERROR_CODE_BAD_REQUEST, 3059 "failed to decrypt session bundle"); 3060 return; 3061 } 3062 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 3063 // Account type cannot be null. This should not happen if session bundle was created 3064 // properly by #StartAccountSession. 3065 if (TextUtils.isEmpty(accountType)) { 3066 sendErrorResponse( 3067 response, 3068 AccountManager.ERROR_CODE_BAD_ARGUMENTS, 3069 "accountType is empty"); 3070 return; 3071 } 3072 3073 // If by any chances, decryptedBundle contains colliding keys with 3074 // system info 3075 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or 3076 // update credentials flow, we should replace with the new values of the current call. 3077 if (appInfo != null) { 3078 decryptedBundle.putAll(appInfo); 3079 } 3080 3081 // Add info that may be used by add account or update credentials flow. 3082 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid); 3083 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid); 3084 } catch (GeneralSecurityException e) { 3085 if (Log.isLoggable(TAG, Log.DEBUG)) { 3086 Log.v(TAG, "Failed to decrypt session bundle!", e); 3087 } 3088 sendErrorResponse( 3089 response, 3090 AccountManager.ERROR_CODE_BAD_REQUEST, 3091 "failed to decrypt session bundle"); 3092 return; 3093 } 3094 3095 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 3096 sendErrorResponse( 3097 response, 3098 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3099 "User cannot modify accounts of this type (policy)."); 3100 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3101 userId); 3102 return; 3103 } 3104 3105 long identityToken = clearCallingIdentity(); 3106 try { 3107 UserAccounts accounts = getUserAccounts(userId); 3108 logRecordWithUid( 3109 accounts, 3110 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH, 3111 TABLE_ACCOUNTS, 3112 callingUid); 3113 new Session( 3114 accounts, 3115 response, 3116 accountType, 3117 expectActivityLaunch, 3118 true /* stripAuthTokenFromResult */, 3119 null /* accountName */, 3120 false /* authDetailsRequired */, 3121 true /* updateLastAuthenticationTime */) { 3122 @Override 3123 public void run() throws RemoteException { 3124 mAuthenticator.finishSession(this, mAccountType, decryptedBundle); 3125 } 3126 3127 @Override 3128 protected String toDebugString(long now) { 3129 return super.toDebugString(now) 3130 + ", finishSession" 3131 + ", accountType " + accountType; 3132 } 3133 }.bind(); 3134 } finally { 3135 restoreCallingIdentity(identityToken); 3136 } 3137 } 3138 3139 private void showCantAddAccount(int errorCode, int userId) { 3140 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 3141 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 3142 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3143 long identityToken = clearCallingIdentity(); 3144 try { 3145 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId)); 3146 } finally { 3147 restoreCallingIdentity(identityToken); 3148 } 3149 } 3150 3151 @Override 3152 public void confirmCredentialsAsUser( 3153 IAccountManagerResponse response, 3154 final Account account, 3155 final Bundle options, 3156 final boolean expectActivityLaunch, 3157 int userId) { 3158 Bundle.setDefusable(options, true); 3159 int callingUid = Binder.getCallingUid(); 3160 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3161 Log.v(TAG, "confirmCredentials: " + account 3162 + ", response " + response 3163 + ", expectActivityLaunch " + expectActivityLaunch 3164 + ", caller's uid " + callingUid 3165 + ", pid " + Binder.getCallingPid()); 3166 } 3167 // Only allow the system process to read accounts of other users 3168 if (isCrossUser(callingUid, userId)) { 3169 throw new SecurityException( 3170 String.format( 3171 "User %s trying to confirm account credentials for %s" , 3172 UserHandle.getCallingUserId(), 3173 userId)); 3174 } 3175 if (response == null) throw new IllegalArgumentException("response is null"); 3176 if (account == null) throw new IllegalArgumentException("account is null"); 3177 long identityToken = clearCallingIdentity(); 3178 try { 3179 UserAccounts accounts = getUserAccounts(userId); 3180 new Session(accounts, response, account.type, expectActivityLaunch, 3181 true /* stripAuthTokenFromResult */, account.name, 3182 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) { 3183 @Override 3184 public void run() throws RemoteException { 3185 mAuthenticator.confirmCredentials(this, account, options); 3186 } 3187 @Override 3188 protected String toDebugString(long now) { 3189 return super.toDebugString(now) + ", confirmCredentials" 3190 + ", " + account; 3191 } 3192 }.bind(); 3193 } finally { 3194 restoreCallingIdentity(identityToken); 3195 } 3196 } 3197 3198 @Override 3199 public void updateCredentials(IAccountManagerResponse response, final Account account, 3200 final String authTokenType, final boolean expectActivityLaunch, 3201 final Bundle loginOptions) { 3202 Bundle.setDefusable(loginOptions, true); 3203 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3204 Log.v(TAG, "updateCredentials: " + account 3205 + ", response " + response 3206 + ", authTokenType " + authTokenType 3207 + ", expectActivityLaunch " + expectActivityLaunch 3208 + ", caller's uid " + Binder.getCallingUid() 3209 + ", pid " + Binder.getCallingPid()); 3210 } 3211 if (response == null) throw new IllegalArgumentException("response is null"); 3212 if (account == null) throw new IllegalArgumentException("account is null"); 3213 int userId = UserHandle.getCallingUserId(); 3214 long identityToken = clearCallingIdentity(); 3215 try { 3216 UserAccounts accounts = getUserAccounts(userId); 3217 new Session(accounts, response, account.type, expectActivityLaunch, 3218 true /* stripAuthTokenFromResult */, account.name, 3219 false /* authDetailsRequired */, true /* updateLastCredentialTime */) { 3220 @Override 3221 public void run() throws RemoteException { 3222 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 3223 } 3224 @Override 3225 protected String toDebugString(long now) { 3226 if (loginOptions != null) loginOptions.keySet(); 3227 return super.toDebugString(now) + ", updateCredentials" 3228 + ", " + account 3229 + ", authTokenType " + authTokenType 3230 + ", loginOptions " + loginOptions; 3231 } 3232 }.bind(); 3233 } finally { 3234 restoreCallingIdentity(identityToken); 3235 } 3236 } 3237 3238 @Override 3239 public void startUpdateCredentialsSession( 3240 IAccountManagerResponse response, 3241 final Account account, 3242 final String authTokenType, 3243 final boolean expectActivityLaunch, 3244 final Bundle loginOptions) { 3245 Bundle.setDefusable(loginOptions, true); 3246 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3247 Log.v(TAG, 3248 "startUpdateCredentialsSession: " + account + ", response " + response 3249 + ", authTokenType " + authTokenType + ", expectActivityLaunch " 3250 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid() 3251 + ", pid " + Binder.getCallingPid()); 3252 } 3253 if (response == null) { 3254 throw new IllegalArgumentException("response is null"); 3255 } 3256 if (account == null) { 3257 throw new IllegalArgumentException("account is null"); 3258 } 3259 3260 final int uid = Binder.getCallingUid(); 3261 // Only allow system to start session 3262 if (!isSystemUid(uid)) { 3263 String msg = String.format( 3264 "uid %s cannot start update credentials session.", 3265 uid); 3266 throw new SecurityException(msg); 3267 } 3268 3269 int userId = UserHandle.getCallingUserId(); 3270 3271 // Check to see if the Password should be included to the caller. 3272 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 3273 boolean isPasswordForwardingAllowed = isPermitted( 3274 callerPkg, uid, Manifest.permission.GET_PASSWORD); 3275 3276 long identityToken = clearCallingIdentity(); 3277 try { 3278 UserAccounts accounts = getUserAccounts(userId); 3279 new StartAccountSession( 3280 accounts, 3281 response, 3282 account.type, 3283 expectActivityLaunch, 3284 account.name, 3285 false /* authDetailsRequired */, 3286 true /* updateLastCredentialTime */, 3287 isPasswordForwardingAllowed) { 3288 @Override 3289 public void run() throws RemoteException { 3290 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, 3291 loginOptions); 3292 } 3293 3294 @Override 3295 protected String toDebugString(long now) { 3296 if (loginOptions != null) 3297 loginOptions.keySet(); 3298 return super.toDebugString(now) 3299 + ", startUpdateCredentialsSession" 3300 + ", " + account 3301 + ", authTokenType " + authTokenType 3302 + ", loginOptions " + loginOptions; 3303 } 3304 }.bind(); 3305 } finally { 3306 restoreCallingIdentity(identityToken); 3307 } 3308 } 3309 3310 @Override 3311 public void isCredentialsUpdateSuggested( 3312 IAccountManagerResponse response, 3313 final Account account, 3314 final String statusToken) { 3315 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3316 Log.v(TAG, 3317 "isCredentialsUpdateSuggested: " + account + ", response " + response 3318 + ", caller's uid " + Binder.getCallingUid() 3319 + ", pid " + Binder.getCallingPid()); 3320 } 3321 if (response == null) { 3322 throw new IllegalArgumentException("response is null"); 3323 } 3324 if (account == null) { 3325 throw new IllegalArgumentException("account is null"); 3326 } 3327 if (TextUtils.isEmpty(statusToken)) { 3328 throw new IllegalArgumentException("status token is empty"); 3329 } 3330 3331 int uid = Binder.getCallingUid(); 3332 // Only allow system to start session 3333 if (!isSystemUid(uid)) { 3334 String msg = String.format( 3335 "uid %s cannot stat add account session.", 3336 uid); 3337 throw new SecurityException(msg); 3338 } 3339 3340 int usrId = UserHandle.getCallingUserId(); 3341 long identityToken = clearCallingIdentity(); 3342 try { 3343 UserAccounts accounts = getUserAccounts(usrId); 3344 new Session(accounts, response, account.type, false /* expectActivityLaunch */, 3345 false /* stripAuthTokenFromResult */, account.name, 3346 false /* authDetailsRequired */) { 3347 @Override 3348 protected String toDebugString(long now) { 3349 return super.toDebugString(now) + ", isCredentialsUpdateSuggested" 3350 + ", " + account; 3351 } 3352 3353 @Override 3354 public void run() throws RemoteException { 3355 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken); 3356 } 3357 3358 @Override 3359 public void onResult(Bundle result) { 3360 Bundle.setDefusable(result, true); 3361 IAccountManagerResponse response = getResponseAndClose(); 3362 if (response == null) { 3363 return; 3364 } 3365 3366 if (result == null) { 3367 sendErrorResponse( 3368 response, 3369 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3370 "null bundle"); 3371 return; 3372 } 3373 3374 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3375 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 3376 + response); 3377 } 3378 // Check to see if an error occurred. We know if an error occurred because all 3379 // error codes are greater than 0. 3380 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) { 3381 sendErrorResponse(response, 3382 result.getInt(AccountManager.KEY_ERROR_CODE), 3383 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3384 return; 3385 } 3386 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) { 3387 sendErrorResponse( 3388 response, 3389 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3390 "no result in response"); 3391 return; 3392 } 3393 final Bundle newResult = new Bundle(); 3394 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 3395 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 3396 sendResponse(response, newResult); 3397 } 3398 }.bind(); 3399 } finally { 3400 restoreCallingIdentity(identityToken); 3401 } 3402 } 3403 3404 @Override 3405 public void editProperties(IAccountManagerResponse response, final String accountType, 3406 final boolean expectActivityLaunch) { 3407 final int callingUid = Binder.getCallingUid(); 3408 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3409 Log.v(TAG, "editProperties: accountType " + accountType 3410 + ", response " + response 3411 + ", expectActivityLaunch " + expectActivityLaunch 3412 + ", caller's uid " + callingUid 3413 + ", pid " + Binder.getCallingPid()); 3414 } 3415 if (response == null) throw new IllegalArgumentException("response is null"); 3416 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 3417 int userId = UserHandle.getCallingUserId(); 3418 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { 3419 String msg = String.format( 3420 "uid %s cannot edit authenticator properites for account type: %s", 3421 callingUid, 3422 accountType); 3423 throw new SecurityException(msg); 3424 } 3425 long identityToken = clearCallingIdentity(); 3426 try { 3427 UserAccounts accounts = getUserAccounts(userId); 3428 new Session(accounts, response, accountType, expectActivityLaunch, 3429 true /* stripAuthTokenFromResult */, null /* accountName */, 3430 false /* authDetailsRequired */) { 3431 @Override 3432 public void run() throws RemoteException { 3433 mAuthenticator.editProperties(this, mAccountType); 3434 } 3435 @Override 3436 protected String toDebugString(long now) { 3437 return super.toDebugString(now) + ", editProperties" 3438 + ", accountType " + accountType; 3439 } 3440 }.bind(); 3441 } finally { 3442 restoreCallingIdentity(identityToken); 3443 } 3444 } 3445 3446 @Override 3447 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName, 3448 @NonNull UserHandle userHandle) { 3449 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 3450 throw new SecurityException("Can be called only by system UID"); 3451 } 3452 Preconditions.checkNotNull(account, "account cannot be null"); 3453 Preconditions.checkNotNull(packageName, "packageName cannot be null"); 3454 Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); 3455 3456 final int userId = userHandle.getIdentifier(); 3457 3458 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 3459 3460 try { 3461 3462 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); 3463 // Use null token which means any token. Having a token means the package 3464 // is trusted by the authenticator, hence it is fine to access the account. 3465 if (permissionIsGranted(account, null, uid, userId)) { 3466 return true; 3467 } 3468 // In addition to the permissions required to get an auth token we also allow 3469 // the account to be accessed by holders of the get accounts permissions. 3470 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) 3471 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); 3472 } catch (NameNotFoundException e) { 3473 return false; 3474 } 3475 } 3476 3477 private boolean checkUidPermission(String permission, int uid, String opPackageName) { 3478 final long identity = Binder.clearCallingIdentity(); 3479 try { 3480 IPackageManager pm = ActivityThread.getPackageManager(); 3481 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) { 3482 return false; 3483 } 3484 final int opCode = AppOpsManager.permissionToOpCode(permission); 3485 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( 3486 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED); 3487 } catch (RemoteException e) { 3488 /* ignore - local call */ 3489 } finally { 3490 Binder.restoreCallingIdentity(identity); 3491 } 3492 return false; 3493 } 3494 3495 @Override 3496 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account, 3497 @NonNull String packageName, @NonNull UserHandle userHandle) { 3498 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 3499 throw new SecurityException("Can be called only by system UID"); 3500 } 3501 3502 Preconditions.checkNotNull(account, "account cannot be null"); 3503 Preconditions.checkNotNull(packageName, "packageName cannot be null"); 3504 Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); 3505 3506 final int userId = userHandle.getIdentifier(); 3507 3508 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 3509 3510 final int uid; 3511 try { 3512 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 3513 } catch (NameNotFoundException e) { 3514 Slog.e(TAG, "Unknown package " + packageName); 3515 return null; 3516 } 3517 3518 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null); 3519 3520 return PendingIntent.getActivityAsUser( 3521 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT 3522 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 3523 null, new UserHandle(userId)).getIntentSender(); 3524 } 3525 3526 private Intent newRequestAccountAccessIntent(Account account, String packageName, 3527 int uid, RemoteCallback callback) { 3528 return newGrantCredentialsPermissionIntent(account, packageName, uid, 3529 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() { 3530 @Override 3531 public void onResult(Bundle value) throws RemoteException { 3532 handleAuthenticatorResponse(true); 3533 } 3534 3535 @Override 3536 public void onRequestContinued() { 3537 /* ignore */ 3538 } 3539 3540 @Override 3541 public void onError(int errorCode, String errorMessage) throws RemoteException { 3542 handleAuthenticatorResponse(false); 3543 } 3544 3545 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { 3546 cancelNotification(getCredentialPermissionNotificationId(account, 3547 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, 3548 UserHandle.getUserHandleForUid(uid)); 3549 if (callback != null) { 3550 Bundle result = new Bundle(); 3551 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted); 3552 callback.sendResult(result); 3553 } 3554 } 3555 }), AccountManager.ACCOUNT_ACCESS_TOKEN, false); 3556 } 3557 3558 @Override 3559 public boolean someUserHasAccount(@NonNull final Account account) { 3560 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) { 3561 throw new SecurityException("Only system can check for accounts across users"); 3562 } 3563 final long token = Binder.clearCallingIdentity(); 3564 try { 3565 AccountAndUser[] allAccounts = getAllAccounts(); 3566 for (int i = allAccounts.length - 1; i >= 0; i--) { 3567 if (allAccounts[i].account.equals(account)) { 3568 return true; 3569 } 3570 } 3571 return false; 3572 } finally { 3573 Binder.restoreCallingIdentity(token); 3574 } 3575 } 3576 3577 private class GetAccountsByTypeAndFeatureSession extends Session { 3578 private final String[] mFeatures; 3579 private volatile Account[] mAccountsOfType = null; 3580 private volatile ArrayList<Account> mAccountsWithFeatures = null; 3581 private volatile int mCurrentAccount = 0; 3582 private final int mCallingUid; 3583 3584 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 3585 IAccountManagerResponse response, String type, String[] features, int callingUid) { 3586 super(accounts, response, type, false /* expectActivityLaunch */, 3587 true /* stripAuthTokenFromResult */, null /* accountName */, 3588 false /* authDetailsRequired */); 3589 mCallingUid = callingUid; 3590 mFeatures = features; 3591 } 3592 3593 @Override 3594 public void run() throws RemoteException { 3595 synchronized (mAccounts.cacheLock) { 3596 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 3597 null); 3598 } 3599 // check whether each account matches the requested features 3600 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 3601 mCurrentAccount = 0; 3602 3603 checkAccount(); 3604 } 3605 3606 public void checkAccount() { 3607 if (mCurrentAccount >= mAccountsOfType.length) { 3608 sendResult(); 3609 return; 3610 } 3611 3612 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 3613 if (accountAuthenticator == null) { 3614 // It is possible that the authenticator has died, which is indicated by 3615 // mAuthenticator being set to null. If this happens then just abort. 3616 // There is no need to send back a result or error in this case since 3617 // that already happened when mAuthenticator was cleared. 3618 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3619 Log.v(TAG, "checkAccount: aborting session since we are no longer" 3620 + " connected to the authenticator, " + toDebugString()); 3621 } 3622 return; 3623 } 3624 try { 3625 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 3626 } catch (RemoteException e) { 3627 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 3628 } 3629 } 3630 3631 @Override 3632 public void onResult(Bundle result) { 3633 Bundle.setDefusable(result, true); 3634 mNumResults++; 3635 if (result == null) { 3636 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 3637 return; 3638 } 3639 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 3640 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 3641 } 3642 mCurrentAccount++; 3643 checkAccount(); 3644 } 3645 3646 public void sendResult() { 3647 IAccountManagerResponse response = getResponseAndClose(); 3648 if (response != null) { 3649 try { 3650 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 3651 for (int i = 0; i < accounts.length; i++) { 3652 accounts[i] = mAccountsWithFeatures.get(i); 3653 } 3654 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3655 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 3656 + response); 3657 } 3658 Bundle result = new Bundle(); 3659 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 3660 response.onResult(result); 3661 } catch (RemoteException e) { 3662 // if the caller is dead then there is no one to care about remote exceptions 3663 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3664 Log.v(TAG, "failure while notifying response", e); 3665 } 3666 } 3667 } 3668 } 3669 3670 3671 @Override 3672 protected String toDebugString(long now) { 3673 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 3674 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 3675 } 3676 } 3677 3678 /** 3679 * Returns the accounts visible to the client within the context of a specific user 3680 * @hide 3681 */ 3682 @NonNull 3683 public Account[] getAccounts(int userId, String opPackageName) { 3684 int callingUid = Binder.getCallingUid(); 3685 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3686 opPackageName); 3687 if (visibleAccountTypes.isEmpty()) { 3688 return new Account[0]; 3689 } 3690 long identityToken = clearCallingIdentity(); 3691 try { 3692 UserAccounts accounts = getUserAccounts(userId); 3693 return getAccountsInternal( 3694 accounts, 3695 callingUid, 3696 null, // packageName 3697 visibleAccountTypes); 3698 } finally { 3699 restoreCallingIdentity(identityToken); 3700 } 3701 } 3702 3703 /** 3704 * Returns accounts for all running users. 3705 * 3706 * @hide 3707 */ 3708 @NonNull 3709 public AccountAndUser[] getRunningAccounts() { 3710 final int[] runningUserIds; 3711 try { 3712 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 3713 } catch (RemoteException e) { 3714 // Running in system_server; should never happen 3715 throw new RuntimeException(e); 3716 } 3717 return getAccounts(runningUserIds); 3718 } 3719 3720 /** {@hide} */ 3721 @NonNull 3722 public AccountAndUser[] getAllAccounts() { 3723 final List<UserInfo> users = getUserManager().getUsers(true); 3724 final int[] userIds = new int[users.size()]; 3725 for (int i = 0; i < userIds.length; i++) { 3726 userIds[i] = users.get(i).id; 3727 } 3728 return getAccounts(userIds); 3729 } 3730 3731 @NonNull 3732 private AccountAndUser[] getAccounts(int[] userIds) { 3733 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 3734 for (int userId : userIds) { 3735 UserAccounts userAccounts = getUserAccounts(userId); 3736 if (userAccounts == null) continue; 3737 synchronized (userAccounts.cacheLock) { 3738 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 3739 Binder.getCallingUid(), null); 3740 for (int a = 0; a < accounts.length; a++) { 3741 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 3742 } 3743 } 3744 } 3745 3746 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 3747 return runningAccounts.toArray(accountsArray); 3748 } 3749 3750 @Override 3751 @NonNull 3752 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { 3753 return getAccountsAsUser(type, userId, null, -1, opPackageName); 3754 } 3755 3756 @NonNull 3757 private Account[] getAccountsAsUser( 3758 String type, 3759 int userId, 3760 String callingPackage, 3761 int packageUid, 3762 String opPackageName) { 3763 int callingUid = Binder.getCallingUid(); 3764 // Only allow the system process to read accounts of other users 3765 if (userId != UserHandle.getCallingUserId() 3766 && callingUid != Process.myUid() 3767 && mContext.checkCallingOrSelfPermission( 3768 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 3769 != PackageManager.PERMISSION_GRANTED) { 3770 throw new SecurityException("User " + UserHandle.getCallingUserId() 3771 + " trying to get account for " + userId); 3772 } 3773 3774 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3775 Log.v(TAG, "getAccounts: accountType " + type 3776 + ", caller's uid " + Binder.getCallingUid() 3777 + ", pid " + Binder.getCallingPid()); 3778 } 3779 // If the original calling app was using the framework account chooser activity, we'll 3780 // be passed in the original caller's uid here, which is what should be used for filtering. 3781 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 3782 callingUid = packageUid; 3783 opPackageName = callingPackage; 3784 } 3785 3786 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3787 opPackageName); 3788 if (visibleAccountTypes.isEmpty() 3789 || (type != null && !visibleAccountTypes.contains(type))) { 3790 return new Account[0]; 3791 } else if (visibleAccountTypes.contains(type)) { 3792 // Prune the list down to just the requested type. 3793 visibleAccountTypes = new ArrayList<>(); 3794 visibleAccountTypes.add(type); 3795 } // else aggregate all the visible accounts (it won't matter if the 3796 // list is empty). 3797 3798 long identityToken = clearCallingIdentity(); 3799 try { 3800 UserAccounts accounts = getUserAccounts(userId); 3801 return getAccountsInternal( 3802 accounts, 3803 callingUid, 3804 callingPackage, 3805 visibleAccountTypes); 3806 } finally { 3807 restoreCallingIdentity(identityToken); 3808 } 3809 } 3810 3811 @NonNull 3812 private Account[] getAccountsInternal( 3813 UserAccounts userAccounts, 3814 int callingUid, 3815 String callingPackage, 3816 List<String> visibleAccountTypes) { 3817 synchronized (userAccounts.cacheLock) { 3818 ArrayList<Account> visibleAccounts = new ArrayList<>(); 3819 for (String visibleType : visibleAccountTypes) { 3820 Account[] accountsForType = getAccountsFromCacheLocked( 3821 userAccounts, visibleType, callingUid, callingPackage); 3822 if (accountsForType != null) { 3823 visibleAccounts.addAll(Arrays.asList(accountsForType)); 3824 } 3825 } 3826 Account[] result = new Account[visibleAccounts.size()]; 3827 for (int i = 0; i < visibleAccounts.size(); i++) { 3828 result[i] = visibleAccounts.get(i); 3829 } 3830 return result; 3831 } 3832 } 3833 3834 @Override 3835 public void addSharedAccountsFromParentUser(int parentUserId, int userId) { 3836 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser"); 3837 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName()); 3838 for (Account account : accounts) { 3839 addSharedAccountAsUser(account, userId); 3840 } 3841 } 3842 3843 private boolean addSharedAccountAsUser(Account account, int userId) { 3844 userId = handleIncomingUser(userId); 3845 UserAccounts accounts = getUserAccounts(userId); 3846 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3847 ContentValues values = new ContentValues(); 3848 values.put(ACCOUNTS_NAME, account.name); 3849 values.put(ACCOUNTS_TYPE, account.type); 3850 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3851 new String[] {account.name, account.type}); 3852 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 3853 if (accountId < 0) { 3854 Log.w(TAG, "insertAccountIntoDatabase: " + account 3855 + ", skipping the DB insert failed"); 3856 return false; 3857 } 3858 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts); 3859 return true; 3860 } 3861 3862 @Override 3863 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 3864 userId = handleIncomingUser(userId); 3865 UserAccounts accounts = getUserAccounts(userId); 3866 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3867 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 3868 final ContentValues values = new ContentValues(); 3869 values.put(ACCOUNTS_NAME, newName); 3870 int r = db.update( 3871 TABLE_SHARED_ACCOUNTS, 3872 values, 3873 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3874 new String[] { account.name, account.type }); 3875 if (r > 0) { 3876 int callingUid = getCallingUid(); 3877 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS, 3878 sharedTableAccountId, accounts, callingUid); 3879 // Recursively rename the account. 3880 renameAccountInternal(accounts, account, newName); 3881 } 3882 return r > 0; 3883 } 3884 3885 @Override 3886 public boolean removeSharedAccountAsUser(Account account, int userId) { 3887 return removeSharedAccountAsUser(account, userId, getCallingUid()); 3888 } 3889 3890 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) { 3891 userId = handleIncomingUser(userId); 3892 UserAccounts accounts = getUserAccounts(userId); 3893 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3894 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 3895 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3896 new String[] {account.name, account.type}); 3897 if (r > 0) { 3898 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS, 3899 sharedTableAccountId, accounts, callingUid); 3900 removeAccountInternal(accounts, account, callingUid); 3901 } 3902 return r > 0; 3903 } 3904 3905 @Override 3906 public Account[] getSharedAccountsAsUser(int userId) { 3907 userId = handleIncomingUser(userId); 3908 UserAccounts accounts = getUserAccounts(userId); 3909 ArrayList<Account> accountList = new ArrayList<>(); 3910 Cursor cursor = null; 3911 try { 3912 cursor = accounts.openHelper.getReadableDatabase() 3913 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 3914 null, null, null, null, null); 3915 if (cursor != null && cursor.moveToFirst()) { 3916 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 3917 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 3918 do { 3919 accountList.add(new Account(cursor.getString(nameIndex), 3920 cursor.getString(typeIndex))); 3921 } while (cursor.moveToNext()); 3922 } 3923 } finally { 3924 if (cursor != null) { 3925 cursor.close(); 3926 } 3927 } 3928 Account[] accountArray = new Account[accountList.size()]; 3929 accountList.toArray(accountArray); 3930 return accountArray; 3931 } 3932 3933 @Override 3934 @NonNull 3935 public Account[] getAccounts(String type, String opPackageName) { 3936 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName); 3937 } 3938 3939 @Override 3940 @NonNull 3941 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { 3942 int callingUid = Binder.getCallingUid(); 3943 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 3944 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 3945 + callingUid + " with uid=" + uid); 3946 } 3947 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid, 3948 opPackageName); 3949 } 3950 3951 @Override 3952 @NonNull 3953 public Account[] getAccountsByTypeForPackage(String type, String packageName, 3954 String opPackageName) { 3955 int packageUid = -1; 3956 try { 3957 packageUid = AppGlobals.getPackageManager().getPackageUid( 3958 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, 3959 UserHandle.getCallingUserId()); 3960 } catch (RemoteException re) { 3961 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 3962 return new Account[0]; 3963 } 3964 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, 3965 packageUid, opPackageName); 3966 } 3967 3968 @Override 3969 public void getAccountsByFeatures( 3970 IAccountManagerResponse response, 3971 String type, 3972 String[] features, 3973 String opPackageName) { 3974 int callingUid = Binder.getCallingUid(); 3975 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3976 Log.v(TAG, "getAccounts: accountType " + type 3977 + ", response " + response 3978 + ", features " + stringArrayToString(features) 3979 + ", caller's uid " + callingUid 3980 + ", pid " + Binder.getCallingPid()); 3981 } 3982 if (response == null) throw new IllegalArgumentException("response is null"); 3983 if (type == null) throw new IllegalArgumentException("accountType is null"); 3984 int userId = UserHandle.getCallingUserId(); 3985 3986 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3987 opPackageName); 3988 if (!visibleAccountTypes.contains(type)) { 3989 Bundle result = new Bundle(); 3990 // Need to return just the accounts that are from matching signatures. 3991 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); 3992 try { 3993 response.onResult(result); 3994 } catch (RemoteException e) { 3995 Log.e(TAG, "Cannot respond to caller do to exception." , e); 3996 } 3997 return; 3998 } 3999 long identityToken = clearCallingIdentity(); 4000 try { 4001 UserAccounts userAccounts = getUserAccounts(userId); 4002 if (features == null || features.length == 0) { 4003 Account[] accounts; 4004 synchronized (userAccounts.cacheLock) { 4005 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 4006 } 4007 Bundle result = new Bundle(); 4008 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 4009 onResult(response, result); 4010 return; 4011 } 4012 new GetAccountsByTypeAndFeatureSession( 4013 userAccounts, 4014 response, 4015 type, 4016 features, 4017 callingUid).bind(); 4018 } finally { 4019 restoreCallingIdentity(identityToken); 4020 } 4021 } 4022 4023 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) { 4024 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID}, 4025 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 4026 try { 4027 if (cursor.moveToNext()) { 4028 return cursor.getLong(0); 4029 } 4030 return -1; 4031 } finally { 4032 cursor.close(); 4033 } 4034 } 4035 4036 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 4037 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 4038 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 4039 try { 4040 if (cursor.moveToNext()) { 4041 return cursor.getLong(0); 4042 } 4043 return -1; 4044 } finally { 4045 cursor.close(); 4046 } 4047 } 4048 4049 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 4050 Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID}, 4051 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 4052 new String[]{key}, null, null, null); 4053 try { 4054 if (cursor.moveToNext()) { 4055 return cursor.getLong(0); 4056 } 4057 return -1; 4058 } finally { 4059 cursor.close(); 4060 } 4061 } 4062 4063 private abstract class Session extends IAccountAuthenticatorResponse.Stub 4064 implements IBinder.DeathRecipient, ServiceConnection { 4065 IAccountManagerResponse mResponse; 4066 final String mAccountType; 4067 final boolean mExpectActivityLaunch; 4068 final long mCreationTime; 4069 final String mAccountName; 4070 // Indicates if we need to add auth details(like last credential time) 4071 final boolean mAuthDetailsRequired; 4072 // If set, we need to update the last authenticated time. This is 4073 // currently 4074 // used on 4075 // successful confirming credentials. 4076 final boolean mUpdateLastAuthenticatedTime; 4077 4078 public int mNumResults = 0; 4079 private int mNumRequestContinued = 0; 4080 private int mNumErrors = 0; 4081 4082 IAccountAuthenticator mAuthenticator = null; 4083 4084 private final boolean mStripAuthTokenFromResult; 4085 protected final UserAccounts mAccounts; 4086 4087 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4088 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4089 boolean authDetailsRequired) { 4090 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult, 4091 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */); 4092 } 4093 4094 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4095 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4096 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) { 4097 super(); 4098 //if (response == null) throw new IllegalArgumentException("response is null"); 4099 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 4100 mAccounts = accounts; 4101 mStripAuthTokenFromResult = stripAuthTokenFromResult; 4102 mResponse = response; 4103 mAccountType = accountType; 4104 mExpectActivityLaunch = expectActivityLaunch; 4105 mCreationTime = SystemClock.elapsedRealtime(); 4106 mAccountName = accountName; 4107 mAuthDetailsRequired = authDetailsRequired; 4108 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; 4109 4110 synchronized (mSessions) { 4111 mSessions.put(toString(), this); 4112 } 4113 if (response != null) { 4114 try { 4115 response.asBinder().linkToDeath(this, 0 /* flags */); 4116 } catch (RemoteException e) { 4117 mResponse = null; 4118 binderDied(); 4119 } 4120 } 4121 } 4122 4123 IAccountManagerResponse getResponseAndClose() { 4124 if (mResponse == null) { 4125 // this session has already been closed 4126 return null; 4127 } 4128 IAccountManagerResponse response = mResponse; 4129 close(); // this clears mResponse so we need to save the response before this call 4130 return response; 4131 } 4132 4133 /** 4134 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our 4135 * security policy. 4136 * 4137 * In particular we want to make sure that the Authenticator doesn't try to trick users 4138 * into launching aribtrary intents on the device via by tricking to click authenticator 4139 * supplied entries in the system Settings app. 4140 */ 4141 protected void checkKeyIntent( 4142 int authUid, 4143 Intent intent) throws SecurityException { 4144 long bid = Binder.clearCallingIdentity(); 4145 try { 4146 PackageManager pm = mContext.getPackageManager(); 4147 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 4148 ActivityInfo targetActivityInfo = resolveInfo.activityInfo; 4149 int targetUid = targetActivityInfo.applicationInfo.uid; 4150 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) { 4151 String pkgName = targetActivityInfo.packageName; 4152 String activityName = targetActivityInfo.name; 4153 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that " 4154 + "does not share a signature with the supplying authenticator (%s)."; 4155 throw new SecurityException( 4156 String.format(tmpl, activityName, pkgName, mAccountType)); 4157 } 4158 } finally { 4159 Binder.restoreCallingIdentity(bid); 4160 } 4161 } 4162 4163 private void close() { 4164 synchronized (mSessions) { 4165 if (mSessions.remove(toString()) == null) { 4166 // the session was already closed, so bail out now 4167 return; 4168 } 4169 } 4170 if (mResponse != null) { 4171 // stop listening for response deaths 4172 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 4173 4174 // clear this so that we don't accidentally send any further results 4175 mResponse = null; 4176 } 4177 cancelTimeout(); 4178 unbind(); 4179 } 4180 4181 @Override 4182 public void binderDied() { 4183 mResponse = null; 4184 close(); 4185 } 4186 4187 protected String toDebugString() { 4188 return toDebugString(SystemClock.elapsedRealtime()); 4189 } 4190 4191 protected String toDebugString(long now) { 4192 return "Session: expectLaunch " + mExpectActivityLaunch 4193 + ", connected " + (mAuthenticator != null) 4194 + ", stats (" + mNumResults + "/" + mNumRequestContinued 4195 + "/" + mNumErrors + ")" 4196 + ", lifetime " + ((now - mCreationTime) / 1000.0); 4197 } 4198 4199 void bind() { 4200 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4201 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 4202 } 4203 if (!bindToAuthenticator(mAccountType)) { 4204 Log.d(TAG, "bind attempt failed for " + toDebugString()); 4205 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 4206 } 4207 } 4208 4209 private void unbind() { 4210 if (mAuthenticator != null) { 4211 mAuthenticator = null; 4212 mContext.unbindService(this); 4213 } 4214 } 4215 4216 public void cancelTimeout() { 4217 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 4218 } 4219 4220 @Override 4221 public void onServiceConnected(ComponentName name, IBinder service) { 4222 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 4223 try { 4224 run(); 4225 } catch (RemoteException e) { 4226 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4227 "remote exception"); 4228 } 4229 } 4230 4231 @Override 4232 public void onServiceDisconnected(ComponentName name) { 4233 mAuthenticator = null; 4234 IAccountManagerResponse response = getResponseAndClose(); 4235 if (response != null) { 4236 try { 4237 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4238 "disconnected"); 4239 } catch (RemoteException e) { 4240 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4241 Log.v(TAG, "Session.onServiceDisconnected: " 4242 + "caught RemoteException while responding", e); 4243 } 4244 } 4245 } 4246 } 4247 4248 public abstract void run() throws RemoteException; 4249 4250 public void onTimedOut() { 4251 IAccountManagerResponse response = getResponseAndClose(); 4252 if (response != null) { 4253 try { 4254 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4255 "timeout"); 4256 } catch (RemoteException e) { 4257 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4258 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 4259 e); 4260 } 4261 } 4262 } 4263 } 4264 4265 @Override 4266 public void onResult(Bundle result) { 4267 Bundle.setDefusable(result, true); 4268 mNumResults++; 4269 Intent intent = null; 4270 if (result != null) { 4271 boolean isSuccessfulConfirmCreds = result.getBoolean( 4272 AccountManager.KEY_BOOLEAN_RESULT, false); 4273 boolean isSuccessfulUpdateCredsOrAddAccount = 4274 result.containsKey(AccountManager.KEY_ACCOUNT_NAME) 4275 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE); 4276 // We should only update lastAuthenticated time, if 4277 // mUpdateLastAuthenticatedTime is true and the confirmRequest 4278 // or updateRequest was successful 4279 boolean needUpdate = mUpdateLastAuthenticatedTime 4280 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount); 4281 if (needUpdate || mAuthDetailsRequired) { 4282 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType); 4283 if (needUpdate && accountPresent) { 4284 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType)); 4285 } 4286 if (mAuthDetailsRequired) { 4287 long lastAuthenticatedTime = -1; 4288 if (accountPresent) { 4289 lastAuthenticatedTime = DatabaseUtils.longForQuery( 4290 mAccounts.openHelper.getReadableDatabase(), 4291 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4292 + " FROM " + 4293 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND " 4294 + ACCOUNTS_TYPE + "=?", 4295 new String[] { 4296 mAccountName, mAccountType 4297 }); 4298 } 4299 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME, 4300 lastAuthenticatedTime); 4301 } 4302 } 4303 } 4304 if (result != null 4305 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 4306 checkKeyIntent( 4307 Binder.getCallingUid(), 4308 intent); 4309 } 4310 if (result != null 4311 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 4312 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 4313 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 4314 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 4315 Account account = new Account(accountName, accountType); 4316 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 4317 new UserHandle(mAccounts.userId)); 4318 } 4319 } 4320 IAccountManagerResponse response; 4321 if (mExpectActivityLaunch && result != null 4322 && result.containsKey(AccountManager.KEY_INTENT)) { 4323 response = mResponse; 4324 } else { 4325 response = getResponseAndClose(); 4326 } 4327 if (response != null) { 4328 try { 4329 if (result == null) { 4330 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4331 Log.v(TAG, getClass().getSimpleName() 4332 + " calling onError() on response " + response); 4333 } 4334 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 4335 "null bundle returned"); 4336 } else { 4337 if (mStripAuthTokenFromResult) { 4338 result.remove(AccountManager.KEY_AUTHTOKEN); 4339 } 4340 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4341 Log.v(TAG, getClass().getSimpleName() 4342 + " calling onResult() on response " + response); 4343 } 4344 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 4345 (intent == null)) { 4346 // All AccountManager error codes are greater than 0 4347 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 4348 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 4349 } else { 4350 response.onResult(result); 4351 } 4352 } 4353 } catch (RemoteException e) { 4354 // if the caller is dead then there is no one to care about remote exceptions 4355 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4356 Log.v(TAG, "failure while notifying response", e); 4357 } 4358 } 4359 } 4360 } 4361 4362 @Override 4363 public void onRequestContinued() { 4364 mNumRequestContinued++; 4365 } 4366 4367 @Override 4368 public void onError(int errorCode, String errorMessage) { 4369 mNumErrors++; 4370 IAccountManagerResponse response = getResponseAndClose(); 4371 if (response != null) { 4372 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4373 Log.v(TAG, getClass().getSimpleName() 4374 + " calling onError() on response " + response); 4375 } 4376 try { 4377 response.onError(errorCode, errorMessage); 4378 } catch (RemoteException e) { 4379 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4380 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 4381 } 4382 } 4383 } else { 4384 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4385 Log.v(TAG, "Session.onError: already closed"); 4386 } 4387 } 4388 } 4389 4390 /** 4391 * find the component name for the authenticator and initiate a bind 4392 * if no authenticator or the bind fails then return false, otherwise return true 4393 */ 4394 private boolean bindToAuthenticator(String authenticatorType) { 4395 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 4396 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 4397 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 4398 if (authenticatorInfo == null) { 4399 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4400 Log.v(TAG, "there is no authenticator for " + authenticatorType 4401 + ", bailing out"); 4402 } 4403 return false; 4404 } 4405 4406 if (!isLocalUnlockedUser(mAccounts.userId) 4407 && !authenticatorInfo.componentInfo.directBootAware) { 4408 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName 4409 + " which isn't encryption aware"); 4410 return false; 4411 } 4412 4413 Intent intent = new Intent(); 4414 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 4415 intent.setComponent(authenticatorInfo.componentName); 4416 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4417 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 4418 } 4419 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 4420 UserHandle.of(mAccounts.userId))) { 4421 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4422 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 4423 } 4424 return false; 4425 } 4426 4427 return true; 4428 } 4429 } 4430 4431 private class MessageHandler extends Handler { 4432 MessageHandler(Looper looper) { 4433 super(looper); 4434 } 4435 4436 @Override 4437 public void handleMessage(Message msg) { 4438 switch (msg.what) { 4439 case MESSAGE_TIMED_OUT: 4440 Session session = (Session)msg.obj; 4441 session.onTimedOut(); 4442 break; 4443 4444 case MESSAGE_COPY_SHARED_ACCOUNT: 4445 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); 4446 break; 4447 4448 default: 4449 throw new IllegalStateException("unhandled message: " + msg.what); 4450 } 4451 } 4452 } 4453 4454 @VisibleForTesting 4455 String getPreNDatabaseName(int userId) { 4456 File systemDir = Environment.getDataSystemDirectory(); 4457 File databaseFile = new File(Environment.getUserSystemDirectory(userId), 4458 PRE_N_DATABASE_NAME); 4459 if (userId == 0) { 4460 // Migrate old file, if it exists, to the new location. 4461 // Make sure the new file doesn't already exist. A dummy file could have been 4462 // accidentally created in the old location, causing the new one to become corrupted 4463 // as well. 4464 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME); 4465 if (oldFile.exists() && !databaseFile.exists()) { 4466 // Check for use directory; create if it doesn't exist, else renameTo will fail 4467 File userDir = Environment.getUserSystemDirectory(userId); 4468 if (!userDir.exists()) { 4469 if (!userDir.mkdirs()) { 4470 throw new IllegalStateException("User dir cannot be created: " + userDir); 4471 } 4472 } 4473 if (!oldFile.renameTo(databaseFile)) { 4474 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 4475 } 4476 } 4477 } 4478 return databaseFile.getPath(); 4479 } 4480 4481 @VisibleForTesting 4482 String getDeDatabaseName(int userId) { 4483 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId), 4484 DE_DATABASE_NAME); 4485 return databaseFile.getPath(); 4486 } 4487 4488 @VisibleForTesting 4489 String getCeDatabaseName(int userId) { 4490 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId), 4491 CE_DATABASE_NAME); 4492 return databaseFile.getPath(); 4493 } 4494 4495 private static class DebugDbHelper{ 4496 private DebugDbHelper() { 4497 } 4498 4499 private static String TABLE_DEBUG = "debug_table"; 4500 4501 // Columns for the table 4502 private static String ACTION_TYPE = "action_type"; 4503 private static String TIMESTAMP = "time"; 4504 private static String CALLER_UID = "caller_uid"; 4505 private static String TABLE_NAME = "table_name"; 4506 private static String KEY = "primary_key"; 4507 4508 // These actions correspond to the occurrence of real actions. Since 4509 // these are called by the authenticators, the uid associated will be 4510 // of the authenticator. 4511 private static String ACTION_SET_PASSWORD = "action_set_password"; 4512 private static String ACTION_CLEAR_PASSWORD = "action_clear_password"; 4513 private static String ACTION_ACCOUNT_ADD = "action_account_add"; 4514 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove"; 4515 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de"; 4516 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove"; 4517 private static String ACTION_ACCOUNT_RENAME = "action_account_rename"; 4518 4519 // These actions don't necessarily correspond to any action on 4520 // accountDb taking place. As an example, there might be a request for 4521 // addingAccount, which might not lead to addition of account on grounds 4522 // of bad authentication. We will still be logging it to keep track of 4523 // who called. 4524 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add"; 4525 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove"; 4526 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts"; 4527 4528 //This action doesn't add account to accountdb. Account is only 4529 // added in finishSession which may be in a different user profile. 4530 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add"; 4531 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH = 4532 "action_called_account_session_finish"; 4533 4534 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 4535 4536 private static void createDebugTable(SQLiteDatabase db) { 4537 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " 4538 + ACCOUNTS_ID + " INTEGER," 4539 + ACTION_TYPE + " TEXT NOT NULL, " 4540 + TIMESTAMP + " DATETIME," 4541 + CALLER_UID + " INTEGER NOT NULL," 4542 + TABLE_NAME + " TEXT NOT NULL," 4543 + KEY + " INTEGER PRIMARY KEY)"); 4544 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")"); 4545 } 4546 } 4547 4548 private void logRecord(UserAccounts accounts, String action, String tableName) { 4549 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4550 logRecord(db, action, tableName, -1, accounts); 4551 } 4552 4553 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) { 4554 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4555 logRecord(db, action, tableName, -1, accounts, uid); 4556 } 4557 4558 /* 4559 * This function receives an opened writable database. 4560 */ 4561 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 4562 UserAccounts userAccount) { 4563 logRecord(db, action, tableName, accountId, userAccount, getCallingUid()); 4564 } 4565 4566 /* 4567 * This function receives an opened writable database. 4568 */ 4569 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 4570 UserAccounts userAccount, int callingUid) { 4571 SQLiteStatement logStatement = userAccount.statementForLogging; 4572 logStatement.bindLong(1, accountId); 4573 logStatement.bindString(2, action); 4574 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date())); 4575 logStatement.bindLong(4, callingUid); 4576 logStatement.bindString(5, tableName); 4577 logStatement.bindLong(6, userAccount.debugDbInsertionPoint); 4578 logStatement.execute(); 4579 logStatement.clearBindings(); 4580 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1) 4581 % MAX_DEBUG_DB_SIZE; 4582 } 4583 4584 /* 4585 * This should only be called once to compile the sql statement for logging 4586 * and to find the insertion point. 4587 */ 4588 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db, 4589 UserAccounts userAccount) { 4590 // Initialize the count if not done earlier. 4591 int size = (int) getDebugTableRowCount(db); 4592 if (size >= MAX_DEBUG_DB_SIZE) { 4593 // Table is full, and we need to find the point where to insert. 4594 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db); 4595 } else { 4596 userAccount.debugDbInsertionPoint = size; 4597 } 4598 compileSqlStatementForLogging(db, userAccount); 4599 } 4600 4601 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) { 4602 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG 4603 + " VALUES (?,?,?,?,?,?)"; 4604 userAccount.statementForLogging = db.compileStatement(sql); 4605 } 4606 4607 private long getDebugTableRowCount(SQLiteDatabase db) { 4608 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG; 4609 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 4610 } 4611 4612 /* 4613 * Finds the row key where the next insertion should take place. This should 4614 * be invoked only if the table has reached its full capacity. 4615 */ 4616 private long getDebugTableInsertionPoint(SQLiteDatabase db) { 4617 // This query finds the smallest timestamp value (and if 2 records have 4618 // same timestamp, the choose the lower id). 4619 String queryCountDebugDbRows = new StringBuilder() 4620 .append("SELECT ").append(DebugDbHelper.KEY) 4621 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG) 4622 .append(" ORDER BY ") 4623 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY) 4624 .append(" LIMIT 1") 4625 .toString(); 4626 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 4627 } 4628 4629 static class PreNDatabaseHelper extends SQLiteOpenHelper { 4630 private final Context mContext; 4631 private final int mUserId; 4632 4633 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) { 4634 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION); 4635 mContext = context; 4636 mUserId = userId; 4637 } 4638 4639 @Override 4640 public void onCreate(SQLiteDatabase db) { 4641 // We use PreNDatabaseHelper only if pre-N db exists 4642 throw new IllegalStateException("Legacy database cannot be created - only upgraded!"); 4643 } 4644 4645 private void createSharedAccountsTable(SQLiteDatabase db) { 4646 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 4647 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4648 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4649 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4650 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4651 } 4652 4653 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) { 4654 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " 4655 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0"); 4656 } 4657 4658 private void addOldAccountNameColumn(SQLiteDatabase db) { 4659 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 4660 } 4661 4662 private void addDebugTable(SQLiteDatabase db) { 4663 DebugDbHelper.createDebugTable(db); 4664 } 4665 4666 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4667 db.execSQL("" 4668 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4669 + " BEGIN" 4670 + " DELETE FROM " + TABLE_AUTHTOKENS 4671 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4672 + " DELETE FROM " + TABLE_EXTRAS 4673 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4674 + " DELETE FROM " + TABLE_GRANTS 4675 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4676 + " END"); 4677 } 4678 4679 private void createGrantsTable(SQLiteDatabase db) { 4680 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 4681 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4682 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 4683 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 4684 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 4685 + "," + GRANTS_GRANTEE_UID + "))"); 4686 } 4687 4688 private void populateMetaTableWithAuthTypeAndUID( 4689 SQLiteDatabase db, 4690 Map<String, Integer> authTypeAndUIDMap) { 4691 Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator(); 4692 while (iterator.hasNext()) { 4693 Entry<String, Integer> entry = iterator.next(); 4694 ContentValues values = new ContentValues(); 4695 values.put(META_KEY, 4696 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey()); 4697 values.put(META_VALUE, entry.getValue()); 4698 db.insert(TABLE_META, null, values); 4699 } 4700 } 4701 4702 /** 4703 * Pre-N database may need an upgrade before splitting 4704 */ 4705 @Override 4706 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 4707 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 4708 4709 if (oldVersion == 1) { 4710 // no longer need to do anything since the work is done 4711 // when upgrading from version 2 4712 oldVersion++; 4713 } 4714 4715 if (oldVersion == 2) { 4716 createGrantsTable(db); 4717 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 4718 createAccountsDeletionTrigger(db); 4719 oldVersion++; 4720 } 4721 4722 if (oldVersion == 3) { 4723 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 4724 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 4725 oldVersion++; 4726 } 4727 4728 if (oldVersion == 4) { 4729 createSharedAccountsTable(db); 4730 oldVersion++; 4731 } 4732 4733 if (oldVersion == 5) { 4734 addOldAccountNameColumn(db); 4735 oldVersion++; 4736 } 4737 4738 if (oldVersion == 6) { 4739 addLastSuccessfullAuthenticatedTimeColumn(db); 4740 oldVersion++; 4741 } 4742 4743 if (oldVersion == 7) { 4744 addDebugTable(db); 4745 oldVersion++; 4746 } 4747 4748 if (oldVersion == 8) { 4749 populateMetaTableWithAuthTypeAndUID( 4750 db, 4751 AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId)); 4752 oldVersion++; 4753 } 4754 4755 if (oldVersion != newVersion) { 4756 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 4757 } 4758 } 4759 4760 @Override 4761 public void onOpen(SQLiteDatabase db) { 4762 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 4763 } 4764 } 4765 4766 static class DeDatabaseHelper extends SQLiteOpenHelper { 4767 4768 private final int mUserId; 4769 private volatile boolean mCeAttached; 4770 4771 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) { 4772 super(context, deDatabaseName, null, DE_DATABASE_VERSION); 4773 mUserId = userId; 4774 } 4775 4776 /** 4777 * This call needs to be made while the mCacheLock is held. The way to 4778 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 4779 * @param db The database. 4780 */ 4781 @Override 4782 public void onCreate(SQLiteDatabase db) { 4783 Log.i(TAG, "Creating DE database for user " + mUserId); 4784 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 4785 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, " 4786 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4787 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4788 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 4789 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, " 4790 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4791 4792 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 4793 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 4794 + META_VALUE + " TEXT)"); 4795 4796 createGrantsTable(db); 4797 createSharedAccountsTable(db); 4798 createAccountsDeletionTrigger(db); 4799 DebugDbHelper.createDebugTable(db); 4800 } 4801 4802 private void createSharedAccountsTable(SQLiteDatabase db) { 4803 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 4804 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4805 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4806 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4807 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4808 } 4809 4810 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4811 db.execSQL("" 4812 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4813 + " BEGIN" 4814 + " DELETE FROM " + TABLE_GRANTS 4815 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4816 + " END"); 4817 } 4818 4819 private void createGrantsTable(SQLiteDatabase db) { 4820 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 4821 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4822 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 4823 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 4824 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 4825 + "," + GRANTS_GRANTEE_UID + "))"); 4826 } 4827 4828 @Override 4829 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 4830 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 4831 4832 if (oldVersion != newVersion) { 4833 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 4834 } 4835 } 4836 4837 public void attachCeDatabase(File ceDbFile) { 4838 SQLiteDatabase db = getWritableDatabase(); 4839 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb"); 4840 mCeAttached = true; 4841 } 4842 4843 public boolean isCeDatabaseAttached() { 4844 return mCeAttached; 4845 } 4846 4847 4848 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() { 4849 if(!mCeAttached) { 4850 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId 4851 + " is still locked. CE database is not yet available.", new Throwable()); 4852 } 4853 return super.getReadableDatabase(); 4854 } 4855 4856 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() { 4857 if(!mCeAttached) { 4858 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId 4859 + " is still locked. CE database is not yet available.", new Throwable()); 4860 } 4861 return super.getWritableDatabase(); 4862 } 4863 4864 @Override 4865 public void onOpen(SQLiteDatabase db) { 4866 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME); 4867 } 4868 4869 private void migratePreNDbToDe(File preNDbFile) { 4870 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile); 4871 SQLiteDatabase db = getWritableDatabase(); 4872 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb"); 4873 db.beginTransaction(); 4874 // Copy accounts fields 4875 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS 4876 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 4877 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4878 + ") " 4879 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 4880 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4881 + " FROM preNDb." + TABLE_ACCOUNTS); 4882 // Copy SHARED_ACCOUNTS 4883 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS 4884 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " + 4885 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 4886 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS); 4887 // Copy DEBUG_TABLE 4888 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG 4889 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + "," 4890 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + "," 4891 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " + 4892 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + "," 4893 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + "," 4894 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY 4895 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG); 4896 // Copy GRANTS 4897 db.execSQL("INSERT INTO " + TABLE_GRANTS 4898 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 4899 + GRANTS_GRANTEE_UID + ") " + 4900 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 4901 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS); 4902 // Copy META 4903 db.execSQL("INSERT INTO " + TABLE_META 4904 + "(" + META_KEY + "," + META_VALUE + ") " 4905 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META); 4906 db.setTransactionSuccessful(); 4907 db.endTransaction(); 4908 4909 db.execSQL("DETACH DATABASE preNDb"); 4910 } 4911 4912 static DeDatabaseHelper create( 4913 Context context, 4914 int userId, 4915 File preNDatabaseFile, 4916 File deDatabaseFile) { 4917 boolean newDbExists = deDatabaseFile.exists(); 4918 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId, 4919 deDatabaseFile.getPath()); 4920 // If the db just created, and there is a legacy db, migrate it 4921 if (!newDbExists && preNDatabaseFile.exists()) { 4922 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION 4923 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId, 4924 preNDatabaseFile.getPath()); 4925 // Open the database to force upgrade if required 4926 preNDatabaseHelper.getWritableDatabase(); 4927 preNDatabaseHelper.close(); 4928 // Move data without SPII to DE 4929 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile); 4930 } 4931 return deDatabaseHelper; 4932 } 4933 } 4934 4935 static class CeDatabaseHelper extends SQLiteOpenHelper { 4936 4937 public CeDatabaseHelper(Context context, String ceDatabaseName) { 4938 super(context, ceDatabaseName, null, CE_DATABASE_VERSION); 4939 } 4940 4941 /** 4942 * This call needs to be made while the mCacheLock is held. 4943 * @param db The database. 4944 */ 4945 @Override 4946 public void onCreate(SQLiteDatabase db) { 4947 Log.i(TAG, "Creating CE database " + getDatabaseName()); 4948 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 4949 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4950 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4951 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4952 + ACCOUNTS_PASSWORD + " TEXT, " 4953 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4954 4955 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 4956 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4957 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4958 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 4959 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 4960 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 4961 4962 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 4963 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4964 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 4965 + EXTRAS_KEY + " TEXT NOT NULL, " 4966 + EXTRAS_VALUE + " TEXT, " 4967 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 4968 4969 createAccountsDeletionTrigger(db); 4970 } 4971 4972 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4973 db.execSQL("" 4974 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4975 + " BEGIN" 4976 + " DELETE FROM " + TABLE_AUTHTOKENS 4977 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4978 + " DELETE FROM " + TABLE_EXTRAS 4979 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4980 + " END"); 4981 } 4982 4983 @Override 4984 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 4985 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion); 4986 4987 if (oldVersion == 9) { 4988 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4989 Log.v(TAG, "onUpgrade upgrading to v10"); 4990 } 4991 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META); 4992 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS); 4993 // Recreate the trigger, since the old one references the table to be removed 4994 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete"); 4995 createAccountsDeletionTrigger(db); 4996 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS); 4997 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG); 4998 oldVersion ++; 4999 } 5000 5001 if (oldVersion != newVersion) { 5002 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 5003 } 5004 } 5005 5006 @Override 5007 public void onOpen(SQLiteDatabase db) { 5008 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME); 5009 } 5010 5011 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name, 5012 String type) { 5013 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, 5014 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 5015 new String[]{name, type}, null, null, null); 5016 try { 5017 if (cursor.moveToNext()) { 5018 return cursor.getString(0); 5019 } 5020 return null; 5021 } finally { 5022 cursor.close(); 5023 } 5024 } 5025 5026 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) { 5027 // Select accounts from CE that do not exist in DE 5028 Cursor cursor = db.rawQuery( 5029 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 5030 + " FROM " + CE_TABLE_ACCOUNTS 5031 + " WHERE NOT EXISTS " 5032 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS 5033 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 5034 + " )", null); 5035 try { 5036 List<Account> accounts = new ArrayList<>(cursor.getCount()); 5037 while (cursor.moveToNext()) { 5038 String accountName = cursor.getString(0); 5039 String accountType = cursor.getString(1); 5040 accounts.add(new Account(accountName, accountType)); 5041 } 5042 return accounts; 5043 } finally { 5044 cursor.close(); 5045 } 5046 } 5047 5048 /** 5049 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location, 5050 * it also performs migration to the new CE database. 5051 * @param context 5052 * @param userId id of the user where the database is located 5053 */ 5054 static CeDatabaseHelper create( 5055 Context context, 5056 int userId, 5057 File preNDatabaseFile, 5058 File ceDatabaseFile) { 5059 boolean newDbExists = ceDatabaseFile.exists(); 5060 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5061 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists=" 5062 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists); 5063 } 5064 boolean removeOldDb = false; 5065 if (!newDbExists && preNDatabaseFile.exists()) { 5066 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile); 5067 } 5068 // Try to open and upgrade if necessary 5069 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath()); 5070 ceHelper.getWritableDatabase(); 5071 ceHelper.close(); 5072 if (removeOldDb) { 5073 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile); 5074 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) { 5075 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile); 5076 } 5077 } 5078 return ceHelper; 5079 } 5080 5081 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) { 5082 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile); 5083 try { 5084 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile); 5085 } catch (IOException e) { 5086 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e); 5087 // Try to remove potentially damaged file if I/O error occurred 5088 deleteDbFileWarnIfFailed(ceDbFile); 5089 return false; 5090 } 5091 return true; 5092 } 5093 } 5094 5095 public IBinder onBind(@SuppressWarnings("unused") Intent intent) { 5096 return asBinder(); 5097 } 5098 5099 /** 5100 * Searches array of arguments for the specified string 5101 * @param args array of argument strings 5102 * @param value value to search for 5103 * @return true if the value is contained in the array 5104 */ 5105 private static boolean scanArgs(String[] args, String value) { 5106 if (args != null) { 5107 for (String arg : args) { 5108 if (value.equals(arg)) { 5109 return true; 5110 } 5111 } 5112 } 5113 return false; 5114 } 5115 5116 @Override 5117 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 5118 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 5119 != PackageManager.PERMISSION_GRANTED) { 5120 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 5121 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 5122 + " without permission " + android.Manifest.permission.DUMP); 5123 return; 5124 } 5125 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 5126 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 5127 5128 final List<UserInfo> users = getUserManager().getUsers(); 5129 for (UserInfo user : users) { 5130 ipw.println("User " + user + ":"); 5131 ipw.increaseIndent(); 5132 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 5133 ipw.println(); 5134 ipw.decreaseIndent(); 5135 } 5136 } 5137 5138 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 5139 String[] args, boolean isCheckinRequest) { 5140 synchronized (userAccounts.cacheLock) { 5141 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 5142 5143 if (isCheckinRequest) { 5144 // This is a checkin request. *Only* upload the account types and the count of each. 5145 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 5146 null, null, ACCOUNTS_TYPE, null, null); 5147 try { 5148 while (cursor.moveToNext()) { 5149 // print type,count 5150 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 5151 } 5152 } finally { 5153 if (cursor != null) { 5154 cursor.close(); 5155 } 5156 } 5157 } else { 5158 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 5159 Process.myUid(), null); 5160 fout.println("Accounts: " + accounts.length); 5161 for (Account account : accounts) { 5162 fout.println(" " + account); 5163 } 5164 5165 // Add debug information. 5166 fout.println(); 5167 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null, 5168 null, null, null, null, DebugDbHelper.TIMESTAMP); 5169 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key"); 5170 fout.println("Accounts History"); 5171 try { 5172 while (cursor.moveToNext()) { 5173 // print type,count 5174 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," + 5175 cursor.getString(2) + "," + cursor.getString(3) + "," 5176 + cursor.getString(4) + "," + cursor.getString(5)); 5177 } 5178 } finally { 5179 cursor.close(); 5180 } 5181 5182 fout.println(); 5183 synchronized (mSessions) { 5184 final long now = SystemClock.elapsedRealtime(); 5185 fout.println("Active Sessions: " + mSessions.size()); 5186 for (Session session : mSessions.values()) { 5187 fout.println(" " + session.toDebugString(now)); 5188 } 5189 } 5190 5191 fout.println(); 5192 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 5193 } 5194 } 5195 } 5196 5197 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 5198 Intent intent, String packageName, final int userId) { 5199 long identityToken = clearCallingIdentity(); 5200 try { 5201 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5202 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 5203 } 5204 5205 if (intent.getComponent() != null && 5206 GrantCredentialsPermissionActivity.class.getName().equals( 5207 intent.getComponent().getClassName())) { 5208 createNoCredentialsPermissionNotification(account, intent, packageName, userId); 5209 } else { 5210 Context contextForUser = getContextForUser(new UserHandle(userId)); 5211 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 5212 intent.addCategory(String.valueOf(notificationId)); 5213 5214 final String notificationTitleFormat = 5215 contextForUser.getText(R.string.notification_title).toString(); 5216 Notification n = new Notification.Builder(contextForUser) 5217 .setWhen(0) 5218 .setSmallIcon(android.R.drawable.stat_sys_warning) 5219 .setColor(contextForUser.getColor( 5220 com.android.internal.R.color.system_notification_accent_color)) 5221 .setContentTitle(String.format(notificationTitleFormat, account.name)) 5222 .setContentText(message) 5223 .setContentIntent(PendingIntent.getActivityAsUser( 5224 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 5225 null, new UserHandle(userId))) 5226 .build(); 5227 installNotification(notificationId, n, packageName, userId); 5228 } 5229 } finally { 5230 restoreCallingIdentity(identityToken); 5231 } 5232 } 5233 5234 @VisibleForTesting 5235 protected void installNotification(int notificationId, final Notification notification, 5236 UserHandle user) { 5237 installNotification(notificationId, notification, "android", user.getIdentifier()); 5238 } 5239 5240 private void installNotification(int notificationId, final Notification notification, 5241 String packageName, int userId) { 5242 final long token = clearCallingIdentity(); 5243 try { 5244 INotificationManager notificationManager = NotificationManager.getService(); 5245 try { 5246 notificationManager.enqueueNotificationWithTag(packageName, packageName, null, 5247 notificationId, notification, new int[1], userId); 5248 } catch (RemoteException e) { 5249 /* ignore - local call */ 5250 } 5251 } finally { 5252 Binder.restoreCallingIdentity(token); 5253 } 5254 } 5255 5256 @VisibleForTesting 5257 protected void cancelNotification(int id, UserHandle user) { 5258 cancelNotification(id, mContext.getPackageName(), user); 5259 } 5260 5261 protected void cancelNotification(int id, String packageName, UserHandle user) { 5262 long identityToken = clearCallingIdentity(); 5263 try { 5264 INotificationManager service = INotificationManager.Stub.asInterface( 5265 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 5266 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier()); 5267 } catch (RemoteException e) { 5268 /* ignore - local call */ 5269 } finally { 5270 restoreCallingIdentity(identityToken); 5271 } 5272 } 5273 5274 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { 5275 for (String perm : permissions) { 5276 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 5277 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5278 Log.v(TAG, " caller uid " + callingUid + " has " + perm); 5279 } 5280 final int opCode = AppOpsManager.permissionToOpCode(perm); 5281 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( 5282 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { 5283 return true; 5284 } 5285 } 5286 } 5287 return false; 5288 } 5289 5290 private int handleIncomingUser(int userId) { 5291 try { 5292 return ActivityManagerNative.getDefault().handleIncomingUser( 5293 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 5294 } catch (RemoteException re) { 5295 // Shouldn't happen, local. 5296 } 5297 return userId; 5298 } 5299 5300 private boolean isPrivileged(int callingUid) { 5301 final int callingUserId = UserHandle.getUserId(callingUid); 5302 5303 final PackageManager userPackageManager; 5304 try { 5305 userPackageManager = mContext.createPackageContextAsUser( 5306 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 5307 } catch (NameNotFoundException e) { 5308 return false; 5309 } 5310 5311 String[] packages = userPackageManager.getPackagesForUid(callingUid); 5312 for (String name : packages) { 5313 try { 5314 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 5315 if (packageInfo != null 5316 && (packageInfo.applicationInfo.privateFlags 5317 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 5318 return true; 5319 } 5320 } catch (PackageManager.NameNotFoundException e) { 5321 return false; 5322 } 5323 } 5324 return false; 5325 } 5326 5327 private boolean permissionIsGranted( 5328 Account account, String authTokenType, int callerUid, int userId) { 5329 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { 5330 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5331 Log.v(TAG, "Access to " + account + " granted calling uid is system"); 5332 } 5333 return true; 5334 } 5335 5336 if (isPrivileged(callerUid)) { 5337 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5338 Log.v(TAG, "Access to " + account + " granted calling uid " 5339 + callerUid + " privileged"); 5340 } 5341 return true; 5342 } 5343 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) { 5344 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5345 Log.v(TAG, "Access to " + account + " granted calling uid " 5346 + callerUid + " manages the account"); 5347 } 5348 return true; 5349 } 5350 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) { 5351 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5352 Log.v(TAG, "Access to " + account + " granted calling uid " 5353 + callerUid + " user granted access"); 5354 } 5355 return true; 5356 } 5357 5358 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5359 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid); 5360 } 5361 5362 return false; 5363 } 5364 5365 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, 5366 String opPackageName) { 5367 if (accountType == null) { 5368 return false; 5369 } else { 5370 return getTypesVisibleToCaller(callingUid, userId, 5371 opPackageName).contains(accountType); 5372 } 5373 } 5374 5375 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { 5376 if (accountType == null) { 5377 return false; 5378 } else { 5379 return getTypesManagedByCaller(callingUid, userId).contains(accountType); 5380 } 5381 } 5382 5383 private List<String> getTypesVisibleToCaller(int callingUid, int userId, 5384 String opPackageName) { 5385 boolean isPermitted = 5386 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, 5387 Manifest.permission.GET_ACCOUNTS_PRIVILEGED); 5388 return getTypesForCaller(callingUid, userId, isPermitted); 5389 } 5390 5391 private List<String> getTypesManagedByCaller(int callingUid, int userId) { 5392 return getTypesForCaller(callingUid, userId, false); 5393 } 5394 5395 private List<String> getTypesForCaller( 5396 int callingUid, int userId, boolean isOtherwisePermitted) { 5397 List<String> managedAccountTypes = new ArrayList<>(); 5398 long identityToken = Binder.clearCallingIdentity(); 5399 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 5400 try { 5401 serviceInfos = mAuthenticatorCache.getAllServices(userId); 5402 } finally { 5403 Binder.restoreCallingIdentity(identityToken); 5404 } 5405 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 5406 serviceInfos) { 5407 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); 5408 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { 5409 managedAccountTypes.add(serviceInfo.type.type); 5410 } 5411 } 5412 return managedAccountTypes; 5413 } 5414 5415 private boolean isAccountPresentForCaller(String accountName, String accountType) { 5416 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) { 5417 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) { 5418 if (account.name.equals(accountName)) { 5419 return true; 5420 } 5421 } 5422 } 5423 return false; 5424 } 5425 5426 private static void checkManageUsersPermission(String message) { 5427 if (ActivityManager.checkComponentPermission( 5428 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true) 5429 != PackageManager.PERMISSION_GRANTED) { 5430 throw new SecurityException("You need MANAGE_USERS permission to: " + message); 5431 } 5432 } 5433 5434 private static void checkManageOrCreateUsersPermission(String message) { 5435 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS, 5436 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED && 5437 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS, 5438 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) { 5439 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: " 5440 + message); 5441 } 5442 } 5443 5444 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 5445 int callerUid) { 5446 if (callerUid == Process.SYSTEM_UID) { 5447 return true; 5448 } 5449 UserAccounts accounts = getUserAccountsForCaller(); 5450 synchronized (accounts.cacheLock) { 5451 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 5452 5453 final String query; 5454 final String[] args; 5455 5456 if (authTokenType != null) { 5457 query = COUNT_OF_MATCHING_GRANTS; 5458 args = new String[] {String.valueOf(callerUid), authTokenType, 5459 account.name, account.type}; 5460 } else { 5461 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN; 5462 args = new String[] {String.valueOf(callerUid), account.name, 5463 account.type}; 5464 } 5465 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0; 5466 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 5467 // TODO: Skip this check when running automated tests. Replace this 5468 // with a more general solution. 5469 Log.d(TAG, "no credentials permission for usage of " + account + ", " 5470 + authTokenType + " by uid " + callerUid 5471 + " but ignoring since device is in test harness."); 5472 return true; 5473 } 5474 return permissionGranted; 5475 } 5476 } 5477 5478 private boolean isSystemUid(int callingUid) { 5479 String[] packages = null; 5480 long ident = Binder.clearCallingIdentity(); 5481 try { 5482 packages = mPackageManager.getPackagesForUid(callingUid); 5483 } finally { 5484 Binder.restoreCallingIdentity(ident); 5485 } 5486 if (packages != null) { 5487 for (String name : packages) { 5488 try { 5489 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); 5490 if (packageInfo != null 5491 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 5492 != 0) { 5493 return true; 5494 } 5495 } catch (PackageManager.NameNotFoundException e) { 5496 Log.w(TAG, String.format("Could not find package [%s]", name), e); 5497 } 5498 } 5499 } else { 5500 Log.w(TAG, "No known packages with uid " + callingUid); 5501 } 5502 return false; 5503 } 5504 5505 /** Succeeds if any of the specified permissions are granted. */ 5506 private void checkReadAccountsPermitted( 5507 int callingUid, 5508 String accountType, 5509 int userId, 5510 String opPackageName) { 5511 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { 5512 String msg = String.format( 5513 "caller uid %s cannot access %s accounts", 5514 callingUid, 5515 accountType); 5516 Log.w(TAG, " " + msg); 5517 throw new SecurityException(msg); 5518 } 5519 } 5520 5521 private boolean canUserModifyAccounts(int userId, int callingUid) { 5522 // the managing app can always modify accounts 5523 if (isProfileOwner(callingUid)) { 5524 return true; 5525 } 5526 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 5527 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 5528 return false; 5529 } 5530 return true; 5531 } 5532 5533 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) { 5534 // the managing app can always modify accounts 5535 if (isProfileOwner(callingUid)) { 5536 return true; 5537 } 5538 DevicePolicyManager dpm = (DevicePolicyManager) mContext 5539 .getSystemService(Context.DEVICE_POLICY_SERVICE); 5540 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 5541 if (typesArray == null) { 5542 return true; 5543 } 5544 for (String forbiddenType : typesArray) { 5545 if (forbiddenType.equals(accountType)) { 5546 return false; 5547 } 5548 } 5549 return true; 5550 } 5551 5552 private boolean isProfileOwner(int uid) { 5553 final DevicePolicyManagerInternal dpmi = 5554 LocalServices.getService(DevicePolicyManagerInternal.class); 5555 return (dpmi != null) 5556 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); 5557 } 5558 5559 @Override 5560 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 5561 throws RemoteException { 5562 final int callingUid = getCallingUid(); 5563 5564 if (callingUid != Process.SYSTEM_UID) { 5565 throw new SecurityException(); 5566 } 5567 5568 if (value) { 5569 grantAppPermission(account, authTokenType, uid); 5570 } else { 5571 revokeAppPermission(account, authTokenType, uid); 5572 } 5573 } 5574 5575 /** 5576 * Allow callers with the given uid permission to get credentials for account/authTokenType. 5577 * <p> 5578 * Although this is public it can only be accessed via the AccountManagerService object 5579 * which is in the system. This means we don't need to protect it with permissions. 5580 * @hide 5581 */ 5582 private void grantAppPermission(Account account, String authTokenType, int uid) { 5583 if (account == null || authTokenType == null) { 5584 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 5585 return; 5586 } 5587 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5588 synchronized (accounts.cacheLock) { 5589 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 5590 db.beginTransaction(); 5591 try { 5592 long accountId = getAccountIdLocked(db, account); 5593 if (accountId >= 0) { 5594 ContentValues values = new ContentValues(); 5595 values.put(GRANTS_ACCOUNTS_ID, accountId); 5596 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 5597 values.put(GRANTS_GRANTEE_UID, uid); 5598 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 5599 db.setTransactionSuccessful(); 5600 } 5601 } finally { 5602 db.endTransaction(); 5603 } 5604 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 5605 UserHandle.of(accounts.userId)); 5606 5607 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); 5608 } 5609 } 5610 5611 /** 5612 * Don't allow callers with the given uid permission to get credentials for 5613 * account/authTokenType. 5614 * <p> 5615 * Although this is public it can only be accessed via the AccountManagerService object 5616 * which is in the system. This means we don't need to protect it with permissions. 5617 * @hide 5618 */ 5619 private void revokeAppPermission(Account account, String authTokenType, int uid) { 5620 if (account == null || authTokenType == null) { 5621 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 5622 return; 5623 } 5624 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5625 synchronized (accounts.cacheLock) { 5626 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 5627 db.beginTransaction(); 5628 try { 5629 long accountId = getAccountIdLocked(db, account); 5630 if (accountId >= 0) { 5631 db.delete(TABLE_GRANTS, 5632 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 5633 + GRANTS_GRANTEE_UID + "=?", 5634 new String[]{String.valueOf(accountId), authTokenType, 5635 String.valueOf(uid)}); 5636 db.setTransactionSuccessful(); 5637 } 5638 } finally { 5639 db.endTransaction(); 5640 } 5641 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 5642 new UserHandle(accounts.userId)); 5643 } 5644 } 5645 5646 static final private String stringArrayToString(String[] value) { 5647 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 5648 } 5649 5650 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 5651 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 5652 if (oldAccountsForType != null) { 5653 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 5654 for (Account curAccount : oldAccountsForType) { 5655 if (!curAccount.equals(account)) { 5656 newAccountsList.add(curAccount); 5657 } 5658 } 5659 if (newAccountsList.isEmpty()) { 5660 accounts.accountCache.remove(account.type); 5661 } else { 5662 Account[] newAccountsForType = new Account[newAccountsList.size()]; 5663 newAccountsForType = newAccountsList.toArray(newAccountsForType); 5664 accounts.accountCache.put(account.type, newAccountsForType); 5665 } 5666 } 5667 accounts.userDataCache.remove(account); 5668 accounts.authTokenCache.remove(account); 5669 accounts.previousNameCache.remove(account); 5670 } 5671 5672 /** 5673 * This assumes that the caller has already checked that the account is not already present. 5674 */ 5675 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 5676 Account[] accountsForType = accounts.accountCache.get(account.type); 5677 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 5678 Account[] newAccountsForType = new Account[oldLength + 1]; 5679 if (accountsForType != null) { 5680 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 5681 } 5682 newAccountsForType[oldLength] = account; 5683 accounts.accountCache.put(account.type, newAccountsForType); 5684 } 5685 5686 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 5687 int callingUid, String callingPackage) { 5688 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 5689 || callingUid == Process.myUid()) { 5690 return unfiltered; 5691 } 5692 UserInfo user = getUserManager().getUserInfo(userAccounts.userId); 5693 if (user != null && user.isRestricted()) { 5694 String[] packages = mPackageManager.getPackagesForUid(callingUid); 5695 // If any of the packages is a white listed package, return the full set, 5696 // otherwise return non-shared accounts only. 5697 // This might be a temporary way to specify a whitelist 5698 String whiteList = mContext.getResources().getString( 5699 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 5700 for (String packageName : packages) { 5701 if (whiteList.contains(";" + packageName + ";")) { 5702 return unfiltered; 5703 } 5704 } 5705 ArrayList<Account> allowed = new ArrayList<Account>(); 5706 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 5707 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 5708 String requiredAccountType = ""; 5709 try { 5710 // If there's an explicit callingPackage specified, check if that package 5711 // opted in to see restricted accounts. 5712 if (callingPackage != null) { 5713 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 5714 if (pi != null && pi.restrictedAccountType != null) { 5715 requiredAccountType = pi.restrictedAccountType; 5716 } 5717 } else { 5718 // Otherwise check if the callingUid has a package that has opted in 5719 for (String packageName : packages) { 5720 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 5721 if (pi != null && pi.restrictedAccountType != null) { 5722 requiredAccountType = pi.restrictedAccountType; 5723 break; 5724 } 5725 } 5726 } 5727 } catch (NameNotFoundException nnfe) { 5728 } 5729 for (Account account : unfiltered) { 5730 if (account.type.equals(requiredAccountType)) { 5731 allowed.add(account); 5732 } else { 5733 boolean found = false; 5734 for (Account shared : sharedAccounts) { 5735 if (shared.equals(account)) { 5736 found = true; 5737 break; 5738 } 5739 } 5740 if (!found) { 5741 allowed.add(account); 5742 } 5743 } 5744 } 5745 Account[] filtered = new Account[allowed.size()]; 5746 allowed.toArray(filtered); 5747 return filtered; 5748 } else { 5749 return unfiltered; 5750 } 5751 } 5752 5753 /* 5754 * packageName can be null. If not null, it should be used to filter out restricted accounts 5755 * that the package is not allowed to access. 5756 */ 5757 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 5758 int callingUid, String callingPackage) { 5759 if (accountType != null) { 5760 final Account[] accounts = userAccounts.accountCache.get(accountType); 5761 if (accounts == null) { 5762 return EMPTY_ACCOUNT_ARRAY; 5763 } else { 5764 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 5765 callingUid, callingPackage); 5766 } 5767 } else { 5768 int totalLength = 0; 5769 for (Account[] accounts : userAccounts.accountCache.values()) { 5770 totalLength += accounts.length; 5771 } 5772 if (totalLength == 0) { 5773 return EMPTY_ACCOUNT_ARRAY; 5774 } 5775 Account[] accounts = new Account[totalLength]; 5776 totalLength = 0; 5777 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 5778 System.arraycopy(accountsOfType, 0, accounts, totalLength, 5779 accountsOfType.length); 5780 totalLength += accountsOfType.length; 5781 } 5782 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 5783 } 5784 } 5785 5786 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 5787 Account account, String key, String value) { 5788 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 5789 if (userDataForAccount == null) { 5790 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 5791 accounts.userDataCache.put(account, userDataForAccount); 5792 } 5793 if (value == null) { 5794 userDataForAccount.remove(key); 5795 } else { 5796 userDataForAccount.put(key, value); 5797 } 5798 } 5799 5800 protected String readCachedTokenInternal( 5801 UserAccounts accounts, 5802 Account account, 5803 String tokenType, 5804 String callingPackage, 5805 byte[] pkgSigDigest) { 5806 synchronized (accounts.cacheLock) { 5807 return accounts.accountTokenCaches.get( 5808 account, tokenType, callingPackage, pkgSigDigest); 5809 } 5810 } 5811 5812 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 5813 Account account, String key, String value) { 5814 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 5815 if (authTokensForAccount == null) { 5816 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 5817 accounts.authTokenCache.put(account, authTokensForAccount); 5818 } 5819 if (value == null) { 5820 authTokensForAccount.remove(key); 5821 } else { 5822 authTokensForAccount.put(key, value); 5823 } 5824 } 5825 5826 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 5827 String authTokenType) { 5828 synchronized (accounts.cacheLock) { 5829 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 5830 if (authTokensForAccount == null) { 5831 // need to populate the cache for this account 5832 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 5833 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 5834 accounts.authTokenCache.put(account, authTokensForAccount); 5835 } 5836 return authTokensForAccount.get(authTokenType); 5837 } 5838 } 5839 5840 protected String readUserDataInternalLocked( 5841 UserAccounts accounts, Account account, String key) { 5842 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 5843 if (userDataForAccount == null) { 5844 // need to populate the cache for this account 5845 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 5846 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 5847 accounts.userDataCache.put(account, userDataForAccount); 5848 } 5849 return userDataForAccount.get(key); 5850 } 5851 5852 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 5853 final SQLiteDatabase db, Account account) { 5854 HashMap<String, String> userDataForAccount = new HashMap<>(); 5855 Cursor cursor = db.query(CE_TABLE_EXTRAS, 5856 COLUMNS_EXTRAS_KEY_AND_VALUE, 5857 SELECTION_USERDATA_BY_ACCOUNT, 5858 new String[]{account.name, account.type}, 5859 null, null, null); 5860 try { 5861 while (cursor.moveToNext()) { 5862 final String tmpkey = cursor.getString(0); 5863 final String value = cursor.getString(1); 5864 userDataForAccount.put(tmpkey, value); 5865 } 5866 } finally { 5867 cursor.close(); 5868 } 5869 return userDataForAccount; 5870 } 5871 5872 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 5873 final SQLiteDatabase db, Account account) { 5874 HashMap<String, String> authTokensForAccount = new HashMap<>(); 5875 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS, 5876 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 5877 SELECTION_AUTHTOKENS_BY_ACCOUNT, 5878 new String[]{account.name, account.type}, 5879 null, null, null); 5880 try { 5881 while (cursor.moveToNext()) { 5882 final String type = cursor.getString(0); 5883 final String authToken = cursor.getString(1); 5884 authTokensForAccount.put(type, authToken); 5885 } 5886 } finally { 5887 cursor.close(); 5888 } 5889 return authTokensForAccount; 5890 } 5891 5892 private Context getContextForUser(UserHandle user) { 5893 try { 5894 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 5895 } catch (NameNotFoundException e) { 5896 // Default to mContext, not finding the package system is running as is unlikely. 5897 return mContext; 5898 } 5899 } 5900 5901 private void sendResponse(IAccountManagerResponse response, Bundle result) { 5902 try { 5903 response.onResult(result); 5904 } catch (RemoteException e) { 5905 // if the caller is dead then there is no one to care about remote 5906 // exceptions 5907 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5908 Log.v(TAG, "failure while notifying response", e); 5909 } 5910 } 5911 } 5912 5913 private void sendErrorResponse(IAccountManagerResponse response, int errorCode, 5914 String errorMessage) { 5915 try { 5916 response.onError(errorCode, errorMessage); 5917 } catch (RemoteException e) { 5918 // if the caller is dead then there is no one to care about remote 5919 // exceptions 5920 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5921 Log.v(TAG, "failure while notifying response", e); 5922 } 5923 } 5924 } 5925 5926 private final class AccountManagerInternalImpl extends AccountManagerInternal { 5927 @Override 5928 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, 5929 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { 5930 if (account == null) { 5931 Slog.w(TAG, "account cannot be null"); 5932 return; 5933 } 5934 if (packageName == null) { 5935 Slog.w(TAG, "packageName cannot be null"); 5936 return; 5937 } 5938 if (userId < UserHandle.USER_SYSTEM) { 5939 Slog.w(TAG, "user id must be concrete"); 5940 return; 5941 } 5942 if (callback == null) { 5943 Slog.w(TAG, "callback cannot be null"); 5944 return; 5945 } 5946 5947 if (hasAccountAccess(account, packageName, new UserHandle(userId))) { 5948 Bundle result = new Bundle(); 5949 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 5950 callback.sendResult(result); 5951 return; 5952 } 5953 5954 final int uid; 5955 try { 5956 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 5957 } catch (NameNotFoundException e) { 5958 Slog.e(TAG, "Unknown package " + packageName); 5959 return; 5960 } 5961 5962 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); 5963 doNotification(mUsers.get(userId), account, null, intent, packageName, userId); 5964 } 5965 } 5966} 5967