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