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