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