AccountManagerService.java revision d63cde7ba4a96d5a07efd48d67e552cadb45d9ce
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accounts; 18 19import android.Manifest; 20import android.accounts.AbstractAccountAuthenticator; 21import android.accounts.Account; 22import android.accounts.AccountAndUser; 23import android.accounts.AccountAuthenticatorResponse; 24import android.accounts.AccountManager; 25import android.accounts.AccountManagerInternal; 26import android.accounts.AuthenticatorDescription; 27import android.accounts.CantAddAccountActivity; 28import android.accounts.GrantCredentialsPermissionActivity; 29import android.accounts.IAccountAccessTracker; 30import android.accounts.IAccountAuthenticator; 31import android.accounts.IAccountAuthenticatorResponse; 32import android.accounts.IAccountManager; 33import android.accounts.IAccountManagerResponse; 34import android.annotation.IntRange; 35import android.annotation.NonNull; 36import android.annotation.Nullable; 37import android.app.ActivityManager; 38import android.app.ActivityManagerNative; 39import android.app.ActivityThread; 40import android.app.AppGlobals; 41import android.app.AppOpsManager; 42import android.app.INotificationManager; 43import android.app.Notification; 44import android.app.NotificationManager; 45import android.app.PendingIntent; 46import android.app.admin.DeviceAdminInfo; 47import android.app.admin.DevicePolicyManager; 48import android.app.admin.DevicePolicyManagerInternal; 49import android.content.BroadcastReceiver; 50import android.content.ComponentName; 51import android.content.ContentValues; 52import android.content.Context; 53import android.content.Intent; 54import android.content.IntentFilter; 55import android.content.IntentSender; 56import android.content.ServiceConnection; 57import android.content.pm.ActivityInfo; 58import android.content.pm.ApplicationInfo; 59import android.content.pm.IPackageManager; 60import android.content.pm.PackageInfo; 61import android.content.pm.PackageManager; 62import android.content.pm.PackageManager.NameNotFoundException; 63import android.content.pm.RegisteredServicesCache; 64import android.content.pm.RegisteredServicesCacheListener; 65import android.content.pm.ResolveInfo; 66import android.content.pm.Signature; 67import android.content.pm.UserInfo; 68import android.database.Cursor; 69import android.database.DatabaseUtils; 70import android.database.sqlite.SQLiteDatabase; 71import android.database.sqlite.SQLiteOpenHelper; 72import android.database.sqlite.SQLiteStatement; 73import android.os.Binder; 74import android.os.Bundle; 75import android.os.Environment; 76import android.os.FileUtils; 77import android.os.Handler; 78import android.os.IBinder; 79import android.os.Looper; 80import android.os.Message; 81import android.os.Parcel; 82import android.os.Process; 83import android.os.RemoteCallback; 84import android.os.RemoteException; 85import android.os.ServiceManager; 86import android.os.SystemClock; 87import android.os.UserHandle; 88import android.os.UserManager; 89import android.os.storage.StorageManager; 90import android.text.TextUtils; 91import android.util.Log; 92import android.util.PackageUtils; 93import android.util.Pair; 94import android.util.Slog; 95import android.util.SparseArray; 96import android.util.SparseBooleanArray; 97 98import com.android.internal.R; 99import com.android.internal.annotations.GuardedBy; 100import com.android.internal.annotations.VisibleForTesting; 101import com.android.internal.content.PackageMonitor; 102import com.android.internal.util.ArrayUtils; 103import com.android.internal.util.IndentingPrintWriter; 104import com.android.internal.util.Preconditions; 105import com.android.server.FgThread; 106import com.android.server.LocalServices; 107import com.android.server.SystemService; 108 109import com.google.android.collect.Lists; 110import com.google.android.collect.Sets; 111 112import java.io.File; 113import java.io.FileDescriptor; 114import java.io.IOException; 115import java.io.PrintWriter; 116import java.security.GeneralSecurityException; 117import java.security.MessageDigest; 118import java.security.NoSuchAlgorithmException; 119import java.text.SimpleDateFormat; 120import java.util.ArrayList; 121import java.util.Arrays; 122import java.util.Collection; 123import java.util.Date; 124import java.util.HashMap; 125import java.util.HashSet; 126import java.util.Iterator; 127import java.util.LinkedHashMap; 128import java.util.List; 129import java.util.Map; 130import java.util.Map.Entry; 131import java.util.concurrent.CopyOnWriteArrayList; 132import java.util.concurrent.atomic.AtomicInteger; 133import 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 */ 143public 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 new AccountAccessTracker()); 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.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER, 1506 resultingAccount.getAccessTracker().asBinder()); 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 */ 1566 insertAccountIntoCacheLocked(accounts, renamedAccount); 1567 /* 1568 * Extract the data and token caches before removing the 1569 * old account to preserve the user data associated with 1570 * the account. 1571 */ 1572 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename); 1573 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename); 1574 removeAccountFromCacheLocked(accounts, accountToRename); 1575 /* 1576 * Update the cached data associated with the renamed 1577 * account. 1578 */ 1579 accounts.userDataCache.put(renamedAccount, tmpData); 1580 accounts.authTokenCache.put(renamedAccount, tmpTokens); 1581 accounts.previousNameCache.put( 1582 renamedAccount, 1583 new AtomicReference<String>(accountToRename.name)); 1584 resultAccount = renamedAccount; 1585 1586 int parentUserId = accounts.userId; 1587 if (canHaveProfile(parentUserId)) { 1588 /* 1589 * Owner or system user account was renamed, rename the account for 1590 * those users with which the account was shared. 1591 */ 1592 List<UserInfo> users = getUserManager().getUsers(true); 1593 for (UserInfo user : users) { 1594 if (user.isRestricted() 1595 && (user.restrictedProfileParentId == parentUserId)) { 1596 renameSharedAccountAsUser(accountToRename, newName, user.id); 1597 } 1598 } 1599 } 1600 sendAccountsChangedBroadcast(accounts.userId); 1601 } 1602 return resultAccount; 1603 } 1604 1605 private boolean canHaveProfile(final int parentUserId) { 1606 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId); 1607 return userInfo != null && userInfo.canHaveProfile(); 1608 } 1609 1610 @Override 1611 public void removeAccount(IAccountManagerResponse response, Account account, 1612 boolean expectActivityLaunch) { 1613 removeAccountAsUser( 1614 response, 1615 account, 1616 expectActivityLaunch, 1617 UserHandle.getCallingUserId()); 1618 } 1619 1620 @Override 1621 public void removeAccountAsUser(IAccountManagerResponse response, Account account, 1622 boolean expectActivityLaunch, int userId) { 1623 final int callingUid = Binder.getCallingUid(); 1624 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1625 Log.v(TAG, "removeAccount: " + account 1626 + ", response " + response 1627 + ", caller's uid " + callingUid 1628 + ", pid " + Binder.getCallingPid() 1629 + ", for user id " + userId); 1630 } 1631 if (response == null) throw new IllegalArgumentException("response is null"); 1632 if (account == null) throw new IllegalArgumentException("account is null"); 1633 // Only allow the system process to modify accounts of other users 1634 if (isCrossUser(callingUid, userId)) { 1635 throw new SecurityException( 1636 String.format( 1637 "User %s tying remove account for %s" , 1638 UserHandle.getCallingUserId(), 1639 userId)); 1640 } 1641 /* 1642 * Only the system or authenticator should be allowed to remove accounts for that 1643 * authenticator. This will let users remove accounts (via Settings in the system) but not 1644 * arbitrary applications (like competing authenticators). 1645 */ 1646 UserHandle user = UserHandle.of(userId); 1647 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) 1648 && !isSystemUid(callingUid)) { 1649 String msg = String.format( 1650 "uid %s cannot remove accounts of type: %s", 1651 callingUid, 1652 account.type); 1653 throw new SecurityException(msg); 1654 } 1655 if (!canUserModifyAccounts(userId, callingUid)) { 1656 try { 1657 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1658 "User cannot modify accounts"); 1659 } catch (RemoteException re) { 1660 } 1661 return; 1662 } 1663 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) { 1664 try { 1665 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1666 "User cannot modify accounts of this type (policy)."); 1667 } catch (RemoteException re) { 1668 } 1669 return; 1670 } 1671 long identityToken = clearCallingIdentity(); 1672 UserAccounts accounts = getUserAccounts(userId); 1673 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 1674 synchronized(accounts.credentialsPermissionNotificationIds) { 1675 for (Pair<Pair<Account, String>, Integer> pair: 1676 accounts.credentialsPermissionNotificationIds.keySet()) { 1677 if (account.equals(pair.first.first)) { 1678 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1679 cancelNotification(id, user); 1680 } 1681 } 1682 } 1683 SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 1684 final long accountId = getAccountIdLocked(db, account); 1685 logRecord( 1686 db, 1687 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, 1688 TABLE_ACCOUNTS, 1689 accountId, 1690 accounts, 1691 callingUid); 1692 try { 1693 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); 1694 } finally { 1695 restoreCallingIdentity(identityToken); 1696 } 1697 } 1698 1699 @Override 1700 public boolean removeAccountExplicitly(Account account) { 1701 final int callingUid = Binder.getCallingUid(); 1702 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1703 Log.v(TAG, "removeAccountExplicitly: " + account 1704 + ", caller's uid " + callingUid 1705 + ", pid " + Binder.getCallingPid()); 1706 } 1707 int userId = Binder.getCallingUserHandle().getIdentifier(); 1708 if (account == null) { 1709 /* 1710 * Null accounts should result in returning false, as per 1711 * AccountManage.addAccountExplicitly(...) java doc. 1712 */ 1713 Log.e(TAG, "account is null"); 1714 return false; 1715 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1716 String msg = String.format( 1717 "uid %s cannot explicitly add accounts of type: %s", 1718 callingUid, 1719 account.type); 1720 throw new SecurityException(msg); 1721 } 1722 UserAccounts accounts = getUserAccountsForCaller(); 1723 SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 1724 final long accountId = getAccountIdLocked(db, account); 1725 logRecord( 1726 db, 1727 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, 1728 TABLE_ACCOUNTS, 1729 accountId, 1730 accounts, 1731 callingUid); 1732 long identityToken = clearCallingIdentity(); 1733 try { 1734 return removeAccountInternal(accounts, account, callingUid); 1735 } finally { 1736 restoreCallingIdentity(identityToken); 1737 } 1738 } 1739 1740 private class RemoveAccountSession extends Session { 1741 final Account mAccount; 1742 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, 1743 Account account, boolean expectActivityLaunch) { 1744 super(accounts, response, account.type, expectActivityLaunch, 1745 true /* stripAuthTokenFromResult */, account.name, 1746 false /* authDetailsRequired */); 1747 mAccount = account; 1748 } 1749 1750 @Override 1751 protected String toDebugString(long now) { 1752 return super.toDebugString(now) + ", removeAccount" 1753 + ", account " + mAccount; 1754 } 1755 1756 @Override 1757 public void run() throws RemoteException { 1758 mAuthenticator.getAccountRemovalAllowed(this, mAccount); 1759 } 1760 1761 @Override 1762 public void onResult(Bundle result) { 1763 Bundle.setDefusable(result, true); 1764 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) 1765 && !result.containsKey(AccountManager.KEY_INTENT)) { 1766 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); 1767 if (removalAllowed) { 1768 removeAccountInternal(mAccounts, mAccount, getCallingUid()); 1769 } 1770 IAccountManagerResponse response = getResponseAndClose(); 1771 if (response != null) { 1772 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1773 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1774 + response); 1775 } 1776 Bundle result2 = new Bundle(); 1777 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); 1778 try { 1779 response.onResult(result2); 1780 } catch (RemoteException e) { 1781 // ignore 1782 } 1783 } 1784 } 1785 super.onResult(result); 1786 } 1787 } 1788 1789 @VisibleForTesting 1790 protected void removeAccountInternal(Account account) { 1791 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid()); 1792 } 1793 1794 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) { 1795 boolean isChanged = false; 1796 boolean userUnlocked = isLocalUnlockedUser(accounts.userId); 1797 if (!userUnlocked) { 1798 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId 1799 + " is still locked. CE data will be removed later"); 1800 } 1801 synchronized (accounts.cacheLock) { 1802 final SQLiteDatabase db = userUnlocked 1803 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked() 1804 : accounts.openHelper.getWritableDatabase(); 1805 db.beginTransaction(); 1806 // Set to a dummy value, this will only be used if the database 1807 // transaction succeeds. 1808 long accountId = -1; 1809 try { 1810 accountId = getAccountIdLocked(db, account); 1811 if (accountId >= 0) { 1812 db.delete( 1813 TABLE_ACCOUNTS, 1814 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 1815 new String[]{ account.name, account.type }); 1816 if (userUnlocked) { 1817 // Delete from CE table 1818 db.delete( 1819 CE_TABLE_ACCOUNTS, 1820 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 1821 new String[]{ account.name, account.type }); 1822 } 1823 db.setTransactionSuccessful(); 1824 isChanged = true; 1825 } 1826 } finally { 1827 db.endTransaction(); 1828 } 1829 if (isChanged) { 1830 removeAccountFromCacheLocked(accounts, account); 1831 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured. 1832 sendAccountsChangedBroadcast(accounts.userId); 1833 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE 1834 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE; 1835 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts); 1836 } 1837 } 1838 long id = Binder.clearCallingIdentity(); 1839 try { 1840 int parentUserId = accounts.userId; 1841 if (canHaveProfile(parentUserId)) { 1842 // Remove from any restricted profiles that are sharing this account. 1843 List<UserInfo> users = getUserManager().getUsers(true); 1844 for (UserInfo user : users) { 1845 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) { 1846 removeSharedAccountAsUser(account, user.id, callingUid); 1847 } 1848 } 1849 } 1850 } finally { 1851 Binder.restoreCallingIdentity(id); 1852 } 1853 1854 if (isChanged) { 1855 synchronized (accounts.credentialsPermissionNotificationIds) { 1856 for (Pair<Pair<Account, String>, Integer> key 1857 : accounts.credentialsPermissionNotificationIds.keySet()) { 1858 if (account.equals(key.first.first) 1859 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) { 1860 final int uid = (Integer) key.second; 1861 mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( 1862 account, uid, false)); 1863 } 1864 } 1865 } 1866 } 1867 1868 return isChanged; 1869 } 1870 1871 @Override 1872 public void invalidateAuthToken(String accountType, String authToken) { 1873 int callerUid = Binder.getCallingUid(); 1874 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1875 Log.v(TAG, "invalidateAuthToken: accountType " + accountType 1876 + ", caller's uid " + callerUid 1877 + ", pid " + Binder.getCallingPid()); 1878 } 1879 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1880 if (authToken == null) throw new IllegalArgumentException("authToken is null"); 1881 int userId = UserHandle.getCallingUserId(); 1882 long identityToken = clearCallingIdentity(); 1883 try { 1884 UserAccounts accounts = getUserAccounts(userId); 1885 synchronized (accounts.cacheLock) { 1886 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1887 db.beginTransaction(); 1888 try { 1889 invalidateAuthTokenLocked(accounts, db, accountType, authToken); 1890 invalidateCustomTokenLocked(accounts, accountType, authToken); 1891 db.setTransactionSuccessful(); 1892 } finally { 1893 db.endTransaction(); 1894 } 1895 } 1896 } finally { 1897 restoreCallingIdentity(identityToken); 1898 } 1899 } 1900 1901 private void invalidateCustomTokenLocked( 1902 UserAccounts accounts, 1903 String accountType, 1904 String authToken) { 1905 if (authToken == null || accountType == null) { 1906 return; 1907 } 1908 // Also wipe out cached token in memory. 1909 accounts.accountTokenCaches.remove(accountType, authToken); 1910 } 1911 1912 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, 1913 String accountType, String authToken) { 1914 if (authToken == null || accountType == null) { 1915 return; 1916 } 1917 Cursor cursor = db.rawQuery( 1918 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID 1919 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 1920 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE 1921 + " FROM " + CE_TABLE_ACCOUNTS 1922 + " JOIN " + CE_TABLE_AUTHTOKENS 1923 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1924 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID 1925 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN 1926 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", 1927 new String[]{authToken, accountType}); 1928 try { 1929 while (cursor.moveToNext()) { 1930 long authTokenId = cursor.getLong(0); 1931 String accountName = cursor.getString(1); 1932 String authTokenType = cursor.getString(2); 1933 db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); 1934 writeAuthTokenIntoCacheLocked( 1935 accounts, 1936 db, 1937 new Account(accountName, accountType), 1938 authTokenType, 1939 null); 1940 } 1941 } finally { 1942 cursor.close(); 1943 } 1944 } 1945 1946 private void saveCachedToken( 1947 UserAccounts accounts, 1948 Account account, 1949 String callerPkg, 1950 byte[] callerSigDigest, 1951 String tokenType, 1952 String token, 1953 long expiryMillis) { 1954 1955 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) { 1956 return; 1957 } 1958 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1959 UserHandle.of(accounts.userId)); 1960 synchronized (accounts.cacheLock) { 1961 accounts.accountTokenCaches.put( 1962 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis); 1963 } 1964 } 1965 1966 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, 1967 String authToken) { 1968 if (account == null || type == null) { 1969 return false; 1970 } 1971 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1972 UserHandle.of(accounts.userId)); 1973 synchronized (accounts.cacheLock) { 1974 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 1975 db.beginTransaction(); 1976 try { 1977 long accountId = getAccountIdLocked(db, account); 1978 if (accountId < 0) { 1979 return false; 1980 } 1981 db.delete(CE_TABLE_AUTHTOKENS, 1982 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", 1983 new String[]{type}); 1984 ContentValues values = new ContentValues(); 1985 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); 1986 values.put(AUTHTOKENS_TYPE, type); 1987 values.put(AUTHTOKENS_AUTHTOKEN, authToken); 1988 if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { 1989 db.setTransactionSuccessful(); 1990 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); 1991 return true; 1992 } 1993 return false; 1994 } finally { 1995 db.endTransaction(); 1996 } 1997 } 1998 } 1999 2000 @Override 2001 public String peekAuthToken(Account account, String authTokenType) { 2002 final int callingUid = Binder.getCallingUid(); 2003 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2004 Log.v(TAG, "peekAuthToken: " + account 2005 + ", authTokenType " + authTokenType 2006 + ", caller's uid " + callingUid 2007 + ", pid " + Binder.getCallingPid()); 2008 } 2009 if (account == null) throw new IllegalArgumentException("account is null"); 2010 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2011 int userId = UserHandle.getCallingUserId(); 2012 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2013 String msg = String.format( 2014 "uid %s cannot peek the authtokens associated with accounts of type: %s", 2015 callingUid, 2016 account.type); 2017 throw new SecurityException(msg); 2018 } 2019 if (!isLocalUnlockedUser(userId)) { 2020 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid " 2021 + callingUid); 2022 return null; 2023 } 2024 long identityToken = clearCallingIdentity(); 2025 try { 2026 UserAccounts accounts = getUserAccounts(userId); 2027 return readAuthTokenInternal(accounts, account, authTokenType); 2028 } finally { 2029 restoreCallingIdentity(identityToken); 2030 } 2031 } 2032 2033 @Override 2034 public void setAuthToken(Account account, String authTokenType, String authToken) { 2035 final int callingUid = Binder.getCallingUid(); 2036 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2037 Log.v(TAG, "setAuthToken: " + account 2038 + ", authTokenType " + authTokenType 2039 + ", caller's uid " + callingUid 2040 + ", pid " + Binder.getCallingPid()); 2041 } 2042 if (account == null) throw new IllegalArgumentException("account is null"); 2043 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2044 int userId = UserHandle.getCallingUserId(); 2045 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2046 String msg = String.format( 2047 "uid %s cannot set auth tokens associated with accounts of type: %s", 2048 callingUid, 2049 account.type); 2050 throw new SecurityException(msg); 2051 } 2052 long identityToken = clearCallingIdentity(); 2053 try { 2054 UserAccounts accounts = getUserAccounts(userId); 2055 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); 2056 } finally { 2057 restoreCallingIdentity(identityToken); 2058 } 2059 } 2060 2061 @Override 2062 public void setPassword(Account account, String password) { 2063 final int callingUid = Binder.getCallingUid(); 2064 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2065 Log.v(TAG, "setAuthToken: " + account 2066 + ", caller's uid " + callingUid 2067 + ", pid " + Binder.getCallingPid()); 2068 } 2069 if (account == null) throw new IllegalArgumentException("account is null"); 2070 int userId = UserHandle.getCallingUserId(); 2071 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2072 String msg = String.format( 2073 "uid %s cannot set secrets for accounts of type: %s", 2074 callingUid, 2075 account.type); 2076 throw new SecurityException(msg); 2077 } 2078 long identityToken = clearCallingIdentity(); 2079 try { 2080 UserAccounts accounts = getUserAccounts(userId); 2081 setPasswordInternal(accounts, account, password, callingUid); 2082 } finally { 2083 restoreCallingIdentity(identityToken); 2084 } 2085 } 2086 2087 private void setPasswordInternal(UserAccounts accounts, Account account, String password, 2088 int callingUid) { 2089 if (account == null) { 2090 return; 2091 } 2092 boolean isChanged = false; 2093 synchronized (accounts.cacheLock) { 2094 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); 2095 db.beginTransaction(); 2096 try { 2097 final ContentValues values = new ContentValues(); 2098 values.put(ACCOUNTS_PASSWORD, password); 2099 final long accountId = getAccountIdLocked(db, account); 2100 if (accountId >= 0) { 2101 final String[] argsAccountId = {String.valueOf(accountId)}; 2102 db.update( 2103 CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 2104 db.delete( 2105 CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); 2106 accounts.authTokenCache.remove(account); 2107 accounts.accountTokenCaches.remove(account); 2108 db.setTransactionSuccessful(); 2109 // If there is an account whose password will be updated and the database 2110 // transactions succeed, then we say that a change has occured. Even if the 2111 // new password is the same as the old and there were no authtokens to delete. 2112 isChanged = true; 2113 String action = (password == null || password.length() == 0) ? 2114 DebugDbHelper.ACTION_CLEAR_PASSWORD 2115 : DebugDbHelper.ACTION_SET_PASSWORD; 2116 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid); 2117 } 2118 } finally { 2119 db.endTransaction(); 2120 if (isChanged) { 2121 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed. 2122 sendAccountsChangedBroadcast(accounts.userId); 2123 } 2124 } 2125 } 2126 } 2127 2128 private void sendAccountsChangedBroadcast(int userId) { 2129 Log.i(TAG, "the accounts changed, sending broadcast of " 2130 + ACCOUNTS_CHANGED_INTENT.getAction()); 2131 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); 2132 } 2133 2134 @Override 2135 public void clearPassword(Account account) { 2136 final int callingUid = Binder.getCallingUid(); 2137 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2138 Log.v(TAG, "clearPassword: " + account 2139 + ", caller's uid " + callingUid 2140 + ", pid " + Binder.getCallingPid()); 2141 } 2142 if (account == null) throw new IllegalArgumentException("account is null"); 2143 int userId = UserHandle.getCallingUserId(); 2144 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2145 String msg = String.format( 2146 "uid %s cannot clear passwords for accounts of type: %s", 2147 callingUid, 2148 account.type); 2149 throw new SecurityException(msg); 2150 } 2151 long identityToken = clearCallingIdentity(); 2152 try { 2153 UserAccounts accounts = getUserAccounts(userId); 2154 setPasswordInternal(accounts, account, null, callingUid); 2155 } finally { 2156 restoreCallingIdentity(identityToken); 2157 } 2158 } 2159 2160 @Override 2161 public void setUserData(Account account, String key, String value) { 2162 final int callingUid = Binder.getCallingUid(); 2163 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2164 Log.v(TAG, "setUserData: " + account 2165 + ", key " + key 2166 + ", caller's uid " + callingUid 2167 + ", pid " + Binder.getCallingPid()); 2168 } 2169 if (key == null) throw new IllegalArgumentException("key is null"); 2170 if (account == null) throw new IllegalArgumentException("account is null"); 2171 int userId = UserHandle.getCallingUserId(); 2172 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2173 String msg = String.format( 2174 "uid %s cannot set user data for accounts of type: %s", 2175 callingUid, 2176 account.type); 2177 throw new SecurityException(msg); 2178 } 2179 long identityToken = clearCallingIdentity(); 2180 try { 2181 UserAccounts accounts = getUserAccounts(userId); 2182 synchronized (accounts.cacheLock) { 2183 if (!accountExistsCacheLocked(accounts, account)) { 2184 return; 2185 } 2186 setUserdataInternalLocked(accounts, account, key, value); 2187 } 2188 } finally { 2189 restoreCallingIdentity(identityToken); 2190 } 2191 } 2192 2193 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) { 2194 if (accounts.accountCache.containsKey(account.type)) { 2195 for (Account acc : accounts.accountCache.get(account.type)) { 2196 if (acc.name.equals(account.name)) { 2197 return true; 2198 } 2199 } 2200 } 2201 return false; 2202 } 2203 2204 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key, 2205 String value) { 2206 if (account == null || key == null) { 2207 return; 2208 } 2209 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2210 db.beginTransaction(); 2211 try { 2212 long accountId = getAccountIdLocked(db, account); 2213 if (accountId < 0) { 2214 return; 2215 } 2216 long extrasId = getExtrasIdLocked(db, accountId, key); 2217 if (extrasId < 0) { 2218 extrasId = insertExtraLocked(db, accountId, key, value); 2219 if (extrasId < 0) { 2220 return; 2221 } 2222 } else { 2223 ContentValues values = new ContentValues(); 2224 values.put(EXTRAS_VALUE, value); 2225 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { 2226 return; 2227 } 2228 } 2229 writeUserDataIntoCacheLocked(accounts, db, account, key, value); 2230 db.setTransactionSuccessful(); 2231 } finally { 2232 db.endTransaction(); 2233 } 2234 } 2235 2236 private void onResult(IAccountManagerResponse response, Bundle result) { 2237 if (result == null) { 2238 Log.e(TAG, "the result is unexpectedly null", new Exception()); 2239 } 2240 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2241 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2242 + response); 2243 } 2244 try { 2245 response.onResult(result); 2246 } catch (RemoteException e) { 2247 // if the caller is dead then there is no one to care about remote 2248 // exceptions 2249 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2250 Log.v(TAG, "failure while notifying response", e); 2251 } 2252 } 2253 } 2254 2255 @Override 2256 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, 2257 final String authTokenType) 2258 throws RemoteException { 2259 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2260 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2261 2262 final int callingUid = getCallingUid(); 2263 clearCallingIdentity(); 2264 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { 2265 throw new SecurityException("can only call from system"); 2266 } 2267 int userId = UserHandle.getUserId(callingUid); 2268 long identityToken = clearCallingIdentity(); 2269 try { 2270 UserAccounts accounts = getUserAccounts(userId); 2271 new Session(accounts, response, accountType, false /* expectActivityLaunch */, 2272 false /* stripAuthTokenFromResult */, null /* accountName */, 2273 false /* authDetailsRequired */) { 2274 @Override 2275 protected String toDebugString(long now) { 2276 return super.toDebugString(now) + ", getAuthTokenLabel" 2277 + ", " + accountType 2278 + ", authTokenType " + authTokenType; 2279 } 2280 2281 @Override 2282 public void run() throws RemoteException { 2283 mAuthenticator.getAuthTokenLabel(this, authTokenType); 2284 } 2285 2286 @Override 2287 public void onResult(Bundle result) { 2288 Bundle.setDefusable(result, true); 2289 if (result != null) { 2290 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); 2291 Bundle bundle = new Bundle(); 2292 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); 2293 super.onResult(bundle); 2294 return; 2295 } else { 2296 super.onResult(result); 2297 } 2298 } 2299 }.bind(); 2300 } finally { 2301 restoreCallingIdentity(identityToken); 2302 } 2303 } 2304 2305 @Override 2306 public void getAuthToken( 2307 IAccountManagerResponse response, 2308 final Account account, 2309 final String authTokenType, 2310 final boolean notifyOnAuthFailure, 2311 final boolean expectActivityLaunch, 2312 final Bundle loginOptions) { 2313 Bundle.setDefusable(loginOptions, true); 2314 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2315 Log.v(TAG, "getAuthToken: " + account 2316 + ", response " + response 2317 + ", authTokenType " + authTokenType 2318 + ", notifyOnAuthFailure " + notifyOnAuthFailure 2319 + ", expectActivityLaunch " + expectActivityLaunch 2320 + ", caller's uid " + Binder.getCallingUid() 2321 + ", pid " + Binder.getCallingPid()); 2322 } 2323 if (response == null) throw new IllegalArgumentException("response is null"); 2324 try { 2325 if (account == null) { 2326 Slog.w(TAG, "getAuthToken called with null account"); 2327 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null"); 2328 return; 2329 } 2330 if (authTokenType == null) { 2331 Slog.w(TAG, "getAuthToken called with null authTokenType"); 2332 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null"); 2333 return; 2334 } 2335 } catch (RemoteException e) { 2336 Slog.w(TAG, "Failed to report error back to the client." + e); 2337 return; 2338 } 2339 int userId = UserHandle.getCallingUserId(); 2340 long ident = Binder.clearCallingIdentity(); 2341 final UserAccounts accounts; 2342 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 2343 try { 2344 accounts = getUserAccounts(userId); 2345 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 2346 AuthenticatorDescription.newKey(account.type), accounts.userId); 2347 } finally { 2348 Binder.restoreCallingIdentity(ident); 2349 } 2350 2351 final boolean customTokens = 2352 authenticatorInfo != null && authenticatorInfo.type.customTokens; 2353 2354 // skip the check if customTokens 2355 final int callerUid = Binder.getCallingUid(); 2356 final boolean permissionGranted = 2357 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); 2358 2359 // Get the calling package. We will use it for the purpose of caching. 2360 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 2361 List<String> callerOwnedPackageNames; 2362 ident = Binder.clearCallingIdentity(); 2363 try { 2364 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid)); 2365 } finally { 2366 Binder.restoreCallingIdentity(ident); 2367 } 2368 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) { 2369 String msg = String.format( 2370 "Uid %s is attempting to illegally masquerade as package %s!", 2371 callerUid, 2372 callerPkg); 2373 throw new SecurityException(msg); 2374 } 2375 2376 // let authenticator know the identity of the caller 2377 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); 2378 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); 2379 2380 if (notifyOnAuthFailure) { 2381 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); 2382 } 2383 2384 long identityToken = clearCallingIdentity(); 2385 try { 2386 // Distill the caller's package signatures into a single digest. 2387 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg); 2388 2389 // if the caller has permission, do the peek. otherwise go the more expensive 2390 // route of starting a Session 2391 if (!customTokens && permissionGranted) { 2392 String authToken = readAuthTokenInternal(accounts, account, authTokenType); 2393 if (authToken != null) { 2394 Bundle result = new Bundle(); 2395 result.putString(AccountManager.KEY_AUTHTOKEN, authToken); 2396 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2397 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2398 onResult(response, result); 2399 return; 2400 } 2401 } 2402 2403 if (customTokens) { 2404 /* 2405 * Look up tokens in the new cache only if the loginOptions don't have parameters 2406 * outside of those expected to be injected by the AccountManager, e.g. 2407 * ANDORID_PACKAGE_NAME. 2408 */ 2409 String token = readCachedTokenInternal( 2410 accounts, 2411 account, 2412 authTokenType, 2413 callerPkg, 2414 callerPkgSigDigest); 2415 if (token != null) { 2416 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2417 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); 2418 } 2419 Bundle result = new Bundle(); 2420 result.putString(AccountManager.KEY_AUTHTOKEN, token); 2421 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2422 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2423 onResult(response, result); 2424 return; 2425 } 2426 } 2427 2428 new Session( 2429 accounts, 2430 response, 2431 account.type, 2432 expectActivityLaunch, 2433 false /* stripAuthTokenFromResult */, 2434 account.name, 2435 false /* authDetailsRequired */) { 2436 @Override 2437 protected String toDebugString(long now) { 2438 if (loginOptions != null) loginOptions.keySet(); 2439 return super.toDebugString(now) + ", getAuthToken" 2440 + ", " + account 2441 + ", authTokenType " + authTokenType 2442 + ", loginOptions " + loginOptions 2443 + ", notifyOnAuthFailure " + notifyOnAuthFailure; 2444 } 2445 2446 @Override 2447 public void run() throws RemoteException { 2448 // If the caller doesn't have permission then create and return the 2449 // "grant permission" intent instead of the "getAuthToken" intent. 2450 if (!permissionGranted) { 2451 mAuthenticator.getAuthTokenLabel(this, authTokenType); 2452 } else { 2453 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); 2454 } 2455 } 2456 2457 @Override 2458 public void onResult(Bundle result) { 2459 Bundle.setDefusable(result, true); 2460 if (result != null) { 2461 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { 2462 Intent intent = newGrantCredentialsPermissionIntent( 2463 account, 2464 null, 2465 callerUid, 2466 new AccountAuthenticatorResponse(this), 2467 authTokenType, 2468 true); 2469 Bundle bundle = new Bundle(); 2470 bundle.putParcelable(AccountManager.KEY_INTENT, intent); 2471 onResult(bundle); 2472 return; 2473 } 2474 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 2475 if (authToken != null) { 2476 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); 2477 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 2478 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { 2479 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 2480 "the type and name should not be empty"); 2481 return; 2482 } 2483 Account resultAccount = new Account(name, type); 2484 if (!customTokens) { 2485 saveAuthTokenToDatabase( 2486 mAccounts, 2487 resultAccount, 2488 authTokenType, 2489 authToken); 2490 } 2491 long expiryMillis = result.getLong( 2492 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L); 2493 if (customTokens 2494 && expiryMillis > System.currentTimeMillis()) { 2495 saveCachedToken( 2496 mAccounts, 2497 account, 2498 callerPkg, 2499 callerPkgSigDigest, 2500 authTokenType, 2501 authToken, 2502 expiryMillis); 2503 } 2504 } 2505 2506 Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 2507 if (intent != null && notifyOnAuthFailure && !customTokens) { 2508 /* 2509 * Make sure that the supplied intent is owned by the authenticator 2510 * giving it to the system. Otherwise a malicious authenticator could 2511 * have users launching arbitrary activities by tricking users to 2512 * interact with malicious notifications. 2513 */ 2514 checkKeyIntent( 2515 Binder.getCallingUid(), 2516 intent); 2517 doNotification(mAccounts, 2518 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), 2519 intent, "android", accounts.userId); 2520 } 2521 } 2522 super.onResult(result); 2523 } 2524 }.bind(); 2525 } finally { 2526 restoreCallingIdentity(identityToken); 2527 } 2528 } 2529 2530 private byte[] calculatePackageSignatureDigest(String callerPkg) { 2531 MessageDigest digester; 2532 try { 2533 digester = MessageDigest.getInstance("SHA-256"); 2534 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 2535 callerPkg, PackageManager.GET_SIGNATURES); 2536 for (Signature sig : pkgInfo.signatures) { 2537 digester.update(sig.toByteArray()); 2538 } 2539 } catch (NoSuchAlgorithmException x) { 2540 Log.wtf(TAG, "SHA-256 should be available", x); 2541 digester = null; 2542 } catch (NameNotFoundException e) { 2543 Log.w(TAG, "Could not find packageinfo for: " + callerPkg); 2544 digester = null; 2545 } 2546 return (digester == null) ? null : digester.digest(); 2547 } 2548 2549 private void createNoCredentialsPermissionNotification(Account account, Intent intent, 2550 String packageName, int userId) { 2551 int uid = intent.getIntExtra( 2552 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); 2553 String authTokenType = intent.getStringExtra( 2554 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); 2555 final String titleAndSubtitle = 2556 mContext.getString(R.string.permission_request_notification_with_subtitle, 2557 account.name); 2558 final int index = titleAndSubtitle.indexOf('\n'); 2559 String title = titleAndSubtitle; 2560 String subtitle = ""; 2561 if (index > 0) { 2562 title = titleAndSubtitle.substring(0, index); 2563 subtitle = titleAndSubtitle.substring(index + 1); 2564 } 2565 UserHandle user = new UserHandle(userId); 2566 Context contextForUser = getContextForUser(user); 2567 Notification n = new Notification.Builder(contextForUser) 2568 .setSmallIcon(android.R.drawable.stat_sys_warning) 2569 .setWhen(0) 2570 .setColor(contextForUser.getColor( 2571 com.android.internal.R.color.system_notification_accent_color)) 2572 .setContentTitle(title) 2573 .setContentText(subtitle) 2574 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, 2575 PendingIntent.FLAG_CANCEL_CURRENT, null, user)) 2576 .build(); 2577 installNotification(getCredentialPermissionNotificationId( 2578 account, authTokenType, uid), n, packageName, user.getIdentifier()); 2579 } 2580 2581 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, 2582 int uid, AccountAuthenticatorResponse response, String authTokenType, 2583 boolean startInNewTask) { 2584 2585 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); 2586 2587 if (startInNewTask) { 2588 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. 2589 // Since it was set in Eclair+ we can't change it without breaking apps using 2590 // the intent from a non-Activity context. This is the default behavior. 2591 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2592 } 2593 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account, 2594 authTokenType, uid) + (packageName != null ? packageName : ""))); 2595 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); 2596 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); 2597 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); 2598 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); 2599 2600 return intent; 2601 } 2602 2603 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, 2604 int uid) { 2605 Integer id; 2606 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 2607 synchronized (accounts.credentialsPermissionNotificationIds) { 2608 final Pair<Pair<Account, String>, Integer> key = 2609 new Pair<Pair<Account, String>, Integer>( 2610 new Pair<Account, String>(account, authTokenType), uid); 2611 id = accounts.credentialsPermissionNotificationIds.get(key); 2612 if (id == null) { 2613 id = mNotificationIds.incrementAndGet(); 2614 accounts.credentialsPermissionNotificationIds.put(key, id); 2615 } 2616 } 2617 return id; 2618 } 2619 2620 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { 2621 Integer id; 2622 synchronized (accounts.signinRequiredNotificationIds) { 2623 id = accounts.signinRequiredNotificationIds.get(account); 2624 if (id == null) { 2625 id = mNotificationIds.incrementAndGet(); 2626 accounts.signinRequiredNotificationIds.put(account, id); 2627 } 2628 } 2629 return id; 2630 } 2631 2632 @Override 2633 public void addAccount(final IAccountManagerResponse response, final String accountType, 2634 final String authTokenType, final String[] requiredFeatures, 2635 final boolean expectActivityLaunch, final Bundle optionsIn) { 2636 Bundle.setDefusable(optionsIn, true); 2637 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2638 Log.v(TAG, "addAccount: accountType " + accountType 2639 + ", response " + response 2640 + ", authTokenType " + authTokenType 2641 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2642 + ", expectActivityLaunch " + expectActivityLaunch 2643 + ", caller's uid " + Binder.getCallingUid() 2644 + ", pid " + Binder.getCallingPid()); 2645 } 2646 if (response == null) throw new IllegalArgumentException("response is null"); 2647 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2648 2649 // Is user disallowed from modifying accounts? 2650 final int uid = Binder.getCallingUid(); 2651 final int userId = UserHandle.getUserId(uid); 2652 if (!canUserModifyAccounts(userId, uid)) { 2653 try { 2654 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2655 "User is not allowed to add an account!"); 2656 } catch (RemoteException re) { 2657 } 2658 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2659 return; 2660 } 2661 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 2662 try { 2663 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2664 "User cannot modify accounts of this type (policy)."); 2665 } catch (RemoteException re) { 2666 } 2667 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2668 userId); 2669 return; 2670 } 2671 2672 final int pid = Binder.getCallingPid(); 2673 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2674 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2675 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2676 2677 int usrId = UserHandle.getCallingUserId(); 2678 long identityToken = clearCallingIdentity(); 2679 try { 2680 UserAccounts accounts = getUserAccounts(usrId); 2681 logRecordWithUid( 2682 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid); 2683 new Session(accounts, response, accountType, expectActivityLaunch, 2684 true /* stripAuthTokenFromResult */, null /* accountName */, 2685 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2686 @Override 2687 public void run() throws RemoteException { 2688 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2689 options); 2690 } 2691 2692 @Override 2693 protected String toDebugString(long now) { 2694 return super.toDebugString(now) + ", addAccount" 2695 + ", accountType " + accountType 2696 + ", requiredFeatures " 2697 + (requiredFeatures != null 2698 ? TextUtils.join(",", requiredFeatures) 2699 : null); 2700 } 2701 }.bind(); 2702 } finally { 2703 restoreCallingIdentity(identityToken); 2704 } 2705 } 2706 2707 @Override 2708 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 2709 final String authTokenType, final String[] requiredFeatures, 2710 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 2711 Bundle.setDefusable(optionsIn, true); 2712 int callingUid = Binder.getCallingUid(); 2713 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2714 Log.v(TAG, "addAccount: accountType " + accountType 2715 + ", response " + response 2716 + ", authTokenType " + authTokenType 2717 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2718 + ", expectActivityLaunch " + expectActivityLaunch 2719 + ", caller's uid " + Binder.getCallingUid() 2720 + ", pid " + Binder.getCallingPid() 2721 + ", for user id " + userId); 2722 } 2723 if (response == null) throw new IllegalArgumentException("response is null"); 2724 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2725 // Only allow the system process to add accounts of other users 2726 if (isCrossUser(callingUid, userId)) { 2727 throw new SecurityException( 2728 String.format( 2729 "User %s trying to add account for %s" , 2730 UserHandle.getCallingUserId(), 2731 userId)); 2732 } 2733 2734 // Is user disallowed from modifying accounts? 2735 if (!canUserModifyAccounts(userId, callingUid)) { 2736 try { 2737 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2738 "User is not allowed to add an account!"); 2739 } catch (RemoteException re) { 2740 } 2741 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2742 return; 2743 } 2744 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 2745 try { 2746 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2747 "User cannot modify accounts of this type (policy)."); 2748 } catch (RemoteException re) { 2749 } 2750 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2751 userId); 2752 return; 2753 } 2754 2755 final int pid = Binder.getCallingPid(); 2756 final int uid = Binder.getCallingUid(); 2757 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2758 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2759 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2760 2761 long identityToken = clearCallingIdentity(); 2762 try { 2763 UserAccounts accounts = getUserAccounts(userId); 2764 logRecordWithUid( 2765 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId); 2766 new Session(accounts, response, accountType, expectActivityLaunch, 2767 true /* stripAuthTokenFromResult */, null /* accountName */, 2768 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2769 @Override 2770 public void run() throws RemoteException { 2771 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2772 options); 2773 } 2774 2775 @Override 2776 protected String toDebugString(long now) { 2777 return super.toDebugString(now) + ", addAccount" 2778 + ", accountType " + accountType 2779 + ", requiredFeatures " 2780 + (requiredFeatures != null 2781 ? TextUtils.join(",", requiredFeatures) 2782 : null); 2783 } 2784 }.bind(); 2785 } finally { 2786 restoreCallingIdentity(identityToken); 2787 } 2788 } 2789 2790 @Override 2791 public void startAddAccountSession( 2792 final IAccountManagerResponse response, 2793 final String accountType, 2794 final String authTokenType, 2795 final String[] requiredFeatures, 2796 final boolean expectActivityLaunch, 2797 final Bundle optionsIn) { 2798 Bundle.setDefusable(optionsIn, true); 2799 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2800 Log.v(TAG, 2801 "startAddAccountSession: accountType " + accountType 2802 + ", response " + response 2803 + ", authTokenType " + authTokenType 2804 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2805 + ", expectActivityLaunch " + expectActivityLaunch 2806 + ", caller's uid " + Binder.getCallingUid() 2807 + ", pid " + Binder.getCallingPid()); 2808 } 2809 if (response == null) { 2810 throw new IllegalArgumentException("response is null"); 2811 } 2812 if (accountType == null) { 2813 throw new IllegalArgumentException("accountType is null"); 2814 } 2815 2816 final int uid = Binder.getCallingUid(); 2817 // Only allow system to start session 2818 if (!isSystemUid(uid)) { 2819 String msg = String.format( 2820 "uid %s cannot stat add account session.", 2821 uid); 2822 throw new SecurityException(msg); 2823 } 2824 2825 final int userId = UserHandle.getUserId(uid); 2826 if (!canUserModifyAccounts(userId, uid)) { 2827 try { 2828 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2829 "User is not allowed to add an account!"); 2830 } catch (RemoteException re) { 2831 } 2832 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2833 return; 2834 } 2835 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 2836 try { 2837 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2838 "User cannot modify accounts of this type (policy)."); 2839 } catch (RemoteException re) { 2840 } 2841 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2842 userId); 2843 return; 2844 } 2845 final int pid = Binder.getCallingPid(); 2846 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2847 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2848 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2849 2850 // Check to see if the Password should be included to the caller. 2851 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 2852 boolean isPasswordForwardingAllowed = isPermitted( 2853 callerPkg, uid, Manifest.permission.GET_PASSWORD); 2854 2855 int usrId = UserHandle.getCallingUserId(); 2856 long identityToken = clearCallingIdentity(); 2857 try { 2858 UserAccounts accounts = getUserAccounts(usrId); 2859 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD, 2860 TABLE_ACCOUNTS, uid); 2861 new StartAccountSession( 2862 accounts, 2863 response, 2864 accountType, 2865 expectActivityLaunch, 2866 null /* accountName */, 2867 false /* authDetailsRequired */, 2868 true /* updateLastAuthenticationTime */, 2869 isPasswordForwardingAllowed) { 2870 @Override 2871 public void run() throws RemoteException { 2872 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType, 2873 requiredFeatures, options); 2874 } 2875 2876 @Override 2877 protected String toDebugString(long now) { 2878 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures); 2879 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType " 2880 + accountType + ", requiredFeatures " 2881 + (requiredFeatures != null ? requiredFeaturesStr : null); 2882 } 2883 }.bind(); 2884 } finally { 2885 restoreCallingIdentity(identityToken); 2886 } 2887 } 2888 2889 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */ 2890 private abstract class StartAccountSession extends Session { 2891 2892 private final boolean mIsPasswordForwardingAllowed; 2893 2894 public StartAccountSession( 2895 UserAccounts accounts, 2896 IAccountManagerResponse response, 2897 String accountType, 2898 boolean expectActivityLaunch, 2899 String accountName, 2900 boolean authDetailsRequired, 2901 boolean updateLastAuthenticationTime, 2902 boolean isPasswordForwardingAllowed) { 2903 super(accounts, response, accountType, expectActivityLaunch, 2904 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired, 2905 updateLastAuthenticationTime); 2906 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed; 2907 } 2908 2909 @Override 2910 public void onResult(Bundle result) { 2911 Bundle.setDefusable(result, true); 2912 mNumResults++; 2913 Intent intent = null; 2914 if (result != null 2915 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 2916 checkKeyIntent( 2917 Binder.getCallingUid(), 2918 intent); 2919 // Omit passwords if the caller isn't permitted to see them. 2920 if (!mIsPasswordForwardingAllowed) { 2921 result.remove(AccountManager.KEY_PASSWORD); 2922 } 2923 } 2924 IAccountManagerResponse response; 2925 if (mExpectActivityLaunch && result != null 2926 && result.containsKey(AccountManager.KEY_INTENT)) { 2927 response = mResponse; 2928 } else { 2929 response = getResponseAndClose(); 2930 } 2931 if (response == null) { 2932 return; 2933 } 2934 if (result == null) { 2935 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2936 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response " 2937 + response); 2938 } 2939 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 2940 "null bundle returned"); 2941 return; 2942 } 2943 2944 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) { 2945 // All AccountManager error codes are greater 2946 // than 0 2947 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE), 2948 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 2949 return; 2950 } 2951 2952 // Strip auth token from result. 2953 result.remove(AccountManager.KEY_AUTHTOKEN); 2954 2955 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2956 Log.v(TAG, 2957 getClass().getSimpleName() + " calling onResult() on response " + response); 2958 } 2959 2960 // Get the session bundle created by authenticator. The 2961 // bundle contains data necessary for finishing the session 2962 // later. The session bundle will be encrypted here and 2963 // decrypted later when trying to finish the session. 2964 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 2965 if (sessionBundle != null) { 2966 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 2967 if (TextUtils.isEmpty(accountType) 2968 || !mAccountType.equalsIgnoreCase(accountType)) { 2969 Log.w(TAG, "Account type in session bundle doesn't match request."); 2970 } 2971 // Add accountType info to session bundle. This will 2972 // override any value set by authenticator. 2973 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType); 2974 2975 // Encrypt session bundle before returning to caller. 2976 try { 2977 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 2978 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle); 2979 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle); 2980 } catch (GeneralSecurityException e) { 2981 if (Log.isLoggable(TAG, Log.DEBUG)) { 2982 Log.v(TAG, "Failed to encrypt session bundle!", e); 2983 } 2984 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 2985 "failed to encrypt session bundle"); 2986 return; 2987 } 2988 } 2989 2990 sendResponse(response, result); 2991 } 2992 } 2993 2994 @Override 2995 public void finishSessionAsUser(IAccountManagerResponse response, 2996 @NonNull Bundle sessionBundle, 2997 boolean expectActivityLaunch, 2998 Bundle appInfo, 2999 int userId) { 3000 Bundle.setDefusable(sessionBundle, true); 3001 int callingUid = Binder.getCallingUid(); 3002 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3003 Log.v(TAG, 3004 "finishSession: response "+ response 3005 + ", expectActivityLaunch " + expectActivityLaunch 3006 + ", caller's uid " + callingUid 3007 + ", caller's user id " + UserHandle.getCallingUserId() 3008 + ", pid " + Binder.getCallingPid() 3009 + ", for user id " + userId); 3010 } 3011 if (response == null) { 3012 throw new IllegalArgumentException("response is null"); 3013 } 3014 3015 // Session bundle is the encrypted bundle of the original bundle created by authenticator. 3016 // Account type is added to it before encryption. 3017 if (sessionBundle == null || sessionBundle.size() == 0) { 3018 throw new IllegalArgumentException("sessionBundle is empty"); 3019 } 3020 3021 // Only allow the system process to finish session for other users 3022 if (isCrossUser(callingUid, userId)) { 3023 throw new SecurityException( 3024 String.format( 3025 "User %s trying to finish session for %s without cross user permission", 3026 UserHandle.getCallingUserId(), 3027 userId)); 3028 } 3029 3030 // Only allow system to finish session 3031 if (!isSystemUid(callingUid)) { 3032 String msg = String.format( 3033 "uid %s cannot finish session because it's not system uid.", 3034 callingUid); 3035 throw new SecurityException(msg); 3036 } 3037 3038 if (!canUserModifyAccounts(userId, callingUid)) { 3039 sendErrorResponse(response, 3040 AccountManager.ERROR_CODE_USER_RESTRICTED, 3041 "User is not allowed to add an account!"); 3042 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3043 return; 3044 } 3045 3046 final int pid = Binder.getCallingPid(); 3047 final Bundle decryptedBundle; 3048 final String accountType; 3049 // First decrypt session bundle to get account type for checking permission. 3050 try { 3051 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 3052 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle); 3053 if (decryptedBundle == null) { 3054 sendErrorResponse( 3055 response, 3056 AccountManager.ERROR_CODE_BAD_REQUEST, 3057 "failed to decrypt session bundle"); 3058 return; 3059 } 3060 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 3061 // Account type cannot be null. This should not happen if session bundle was created 3062 // properly by #StartAccountSession. 3063 if (TextUtils.isEmpty(accountType)) { 3064 sendErrorResponse( 3065 response, 3066 AccountManager.ERROR_CODE_BAD_ARGUMENTS, 3067 "accountType is empty"); 3068 return; 3069 } 3070 3071 // If by any chances, decryptedBundle contains colliding keys with 3072 // system info 3073 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or 3074 // update credentials flow, we should replace with the new values of the current call. 3075 if (appInfo != null) { 3076 decryptedBundle.putAll(appInfo); 3077 } 3078 3079 // Add info that may be used by add account or update credentials flow. 3080 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid); 3081 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid); 3082 } catch (GeneralSecurityException e) { 3083 if (Log.isLoggable(TAG, Log.DEBUG)) { 3084 Log.v(TAG, "Failed to decrypt session bundle!", e); 3085 } 3086 sendErrorResponse( 3087 response, 3088 AccountManager.ERROR_CODE_BAD_REQUEST, 3089 "failed to decrypt session bundle"); 3090 return; 3091 } 3092 3093 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 3094 sendErrorResponse( 3095 response, 3096 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3097 "User cannot modify accounts of this type (policy)."); 3098 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3099 userId); 3100 return; 3101 } 3102 3103 long identityToken = clearCallingIdentity(); 3104 try { 3105 UserAccounts accounts = getUserAccounts(userId); 3106 logRecordWithUid( 3107 accounts, 3108 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH, 3109 TABLE_ACCOUNTS, 3110 callingUid); 3111 new Session( 3112 accounts, 3113 response, 3114 accountType, 3115 expectActivityLaunch, 3116 true /* stripAuthTokenFromResult */, 3117 null /* accountName */, 3118 false /* authDetailsRequired */, 3119 true /* updateLastAuthenticationTime */) { 3120 @Override 3121 public void run() throws RemoteException { 3122 mAuthenticator.finishSession(this, mAccountType, decryptedBundle); 3123 } 3124 3125 @Override 3126 protected String toDebugString(long now) { 3127 return super.toDebugString(now) 3128 + ", finishSession" 3129 + ", accountType " + accountType; 3130 } 3131 }.bind(); 3132 } finally { 3133 restoreCallingIdentity(identityToken); 3134 } 3135 } 3136 3137 private void showCantAddAccount(int errorCode, int userId) { 3138 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 3139 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 3140 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3141 long identityToken = clearCallingIdentity(); 3142 try { 3143 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId)); 3144 } finally { 3145 restoreCallingIdentity(identityToken); 3146 } 3147 } 3148 3149 @Override 3150 public void confirmCredentialsAsUser( 3151 IAccountManagerResponse response, 3152 final Account account, 3153 final Bundle options, 3154 final boolean expectActivityLaunch, 3155 int userId) { 3156 Bundle.setDefusable(options, true); 3157 int callingUid = Binder.getCallingUid(); 3158 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3159 Log.v(TAG, "confirmCredentials: " + account 3160 + ", response " + response 3161 + ", expectActivityLaunch " + expectActivityLaunch 3162 + ", caller's uid " + callingUid 3163 + ", pid " + Binder.getCallingPid()); 3164 } 3165 // Only allow the system process to read accounts of other users 3166 if (isCrossUser(callingUid, userId)) { 3167 throw new SecurityException( 3168 String.format( 3169 "User %s trying to confirm account credentials for %s" , 3170 UserHandle.getCallingUserId(), 3171 userId)); 3172 } 3173 if (response == null) throw new IllegalArgumentException("response is null"); 3174 if (account == null) throw new IllegalArgumentException("account is null"); 3175 long identityToken = clearCallingIdentity(); 3176 try { 3177 UserAccounts accounts = getUserAccounts(userId); 3178 new Session(accounts, response, account.type, expectActivityLaunch, 3179 true /* stripAuthTokenFromResult */, account.name, 3180 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) { 3181 @Override 3182 public void run() throws RemoteException { 3183 mAuthenticator.confirmCredentials(this, account, options); 3184 } 3185 @Override 3186 protected String toDebugString(long now) { 3187 return super.toDebugString(now) + ", confirmCredentials" 3188 + ", " + account; 3189 } 3190 }.bind(); 3191 } finally { 3192 restoreCallingIdentity(identityToken); 3193 } 3194 } 3195 3196 @Override 3197 public void updateCredentials(IAccountManagerResponse response, final Account account, 3198 final String authTokenType, final boolean expectActivityLaunch, 3199 final Bundle loginOptions) { 3200 Bundle.setDefusable(loginOptions, true); 3201 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3202 Log.v(TAG, "updateCredentials: " + account 3203 + ", response " + response 3204 + ", authTokenType " + authTokenType 3205 + ", expectActivityLaunch " + expectActivityLaunch 3206 + ", caller's uid " + Binder.getCallingUid() 3207 + ", pid " + Binder.getCallingPid()); 3208 } 3209 if (response == null) throw new IllegalArgumentException("response is null"); 3210 if (account == null) throw new IllegalArgumentException("account is null"); 3211 int userId = UserHandle.getCallingUserId(); 3212 long identityToken = clearCallingIdentity(); 3213 try { 3214 UserAccounts accounts = getUserAccounts(userId); 3215 new Session(accounts, response, account.type, expectActivityLaunch, 3216 true /* stripAuthTokenFromResult */, account.name, 3217 false /* authDetailsRequired */, true /* updateLastCredentialTime */) { 3218 @Override 3219 public void run() throws RemoteException { 3220 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 3221 } 3222 @Override 3223 protected String toDebugString(long now) { 3224 if (loginOptions != null) loginOptions.keySet(); 3225 return super.toDebugString(now) + ", updateCredentials" 3226 + ", " + account 3227 + ", authTokenType " + authTokenType 3228 + ", loginOptions " + loginOptions; 3229 } 3230 }.bind(); 3231 } finally { 3232 restoreCallingIdentity(identityToken); 3233 } 3234 } 3235 3236 @Override 3237 public void startUpdateCredentialsSession( 3238 IAccountManagerResponse response, 3239 final Account account, 3240 final String authTokenType, 3241 final boolean expectActivityLaunch, 3242 final Bundle loginOptions) { 3243 Bundle.setDefusable(loginOptions, true); 3244 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3245 Log.v(TAG, 3246 "startUpdateCredentialsSession: " + account + ", response " + response 3247 + ", authTokenType " + authTokenType + ", expectActivityLaunch " 3248 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid() 3249 + ", pid " + Binder.getCallingPid()); 3250 } 3251 if (response == null) { 3252 throw new IllegalArgumentException("response is null"); 3253 } 3254 if (account == null) { 3255 throw new IllegalArgumentException("account is null"); 3256 } 3257 3258 final int uid = Binder.getCallingUid(); 3259 // Only allow system to start session 3260 if (!isSystemUid(uid)) { 3261 String msg = String.format( 3262 "uid %s cannot start update credentials session.", 3263 uid); 3264 throw new SecurityException(msg); 3265 } 3266 3267 int userId = UserHandle.getCallingUserId(); 3268 3269 // Check to see if the Password should be included to the caller. 3270 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 3271 boolean isPasswordForwardingAllowed = isPermitted( 3272 callerPkg, uid, Manifest.permission.GET_PASSWORD); 3273 3274 long identityToken = clearCallingIdentity(); 3275 try { 3276 UserAccounts accounts = getUserAccounts(userId); 3277 new StartAccountSession( 3278 accounts, 3279 response, 3280 account.type, 3281 expectActivityLaunch, 3282 account.name, 3283 false /* authDetailsRequired */, 3284 true /* updateLastCredentialTime */, 3285 isPasswordForwardingAllowed) { 3286 @Override 3287 public void run() throws RemoteException { 3288 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, 3289 loginOptions); 3290 } 3291 3292 @Override 3293 protected String toDebugString(long now) { 3294 if (loginOptions != null) 3295 loginOptions.keySet(); 3296 return super.toDebugString(now) 3297 + ", startUpdateCredentialsSession" 3298 + ", " + account 3299 + ", authTokenType " + authTokenType 3300 + ", loginOptions " + loginOptions; 3301 } 3302 }.bind(); 3303 } finally { 3304 restoreCallingIdentity(identityToken); 3305 } 3306 } 3307 3308 @Override 3309 public void isCredentialsUpdateSuggested( 3310 IAccountManagerResponse response, 3311 final Account account, 3312 final String statusToken) { 3313 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3314 Log.v(TAG, 3315 "isCredentialsUpdateSuggested: " + account + ", response " + response 3316 + ", caller's uid " + Binder.getCallingUid() 3317 + ", pid " + Binder.getCallingPid()); 3318 } 3319 if (response == null) { 3320 throw new IllegalArgumentException("response is null"); 3321 } 3322 if (account == null) { 3323 throw new IllegalArgumentException("account is null"); 3324 } 3325 if (TextUtils.isEmpty(statusToken)) { 3326 throw new IllegalArgumentException("status token is empty"); 3327 } 3328 3329 int uid = Binder.getCallingUid(); 3330 // Only allow system to start session 3331 if (!isSystemUid(uid)) { 3332 String msg = String.format( 3333 "uid %s cannot stat add account session.", 3334 uid); 3335 throw new SecurityException(msg); 3336 } 3337 3338 int usrId = UserHandle.getCallingUserId(); 3339 long identityToken = clearCallingIdentity(); 3340 try { 3341 UserAccounts accounts = getUserAccounts(usrId); 3342 new Session(accounts, response, account.type, false /* expectActivityLaunch */, 3343 false /* stripAuthTokenFromResult */, account.name, 3344 false /* authDetailsRequired */) { 3345 @Override 3346 protected String toDebugString(long now) { 3347 return super.toDebugString(now) + ", isCredentialsUpdateSuggested" 3348 + ", " + account; 3349 } 3350 3351 @Override 3352 public void run() throws RemoteException { 3353 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken); 3354 } 3355 3356 @Override 3357 public void onResult(Bundle result) { 3358 Bundle.setDefusable(result, true); 3359 IAccountManagerResponse response = getResponseAndClose(); 3360 if (response == null) { 3361 return; 3362 } 3363 3364 if (result == null) { 3365 sendErrorResponse( 3366 response, 3367 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3368 "null bundle"); 3369 return; 3370 } 3371 3372 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3373 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 3374 + response); 3375 } 3376 // Check to see if an error occurred. We know if an error occurred because all 3377 // error codes are greater than 0. 3378 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) { 3379 sendErrorResponse(response, 3380 result.getInt(AccountManager.KEY_ERROR_CODE), 3381 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3382 return; 3383 } 3384 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) { 3385 sendErrorResponse( 3386 response, 3387 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3388 "no result in response"); 3389 return; 3390 } 3391 final Bundle newResult = new Bundle(); 3392 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 3393 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 3394 sendResponse(response, newResult); 3395 } 3396 }.bind(); 3397 } finally { 3398 restoreCallingIdentity(identityToken); 3399 } 3400 } 3401 3402 @Override 3403 public void editProperties(IAccountManagerResponse response, final String accountType, 3404 final boolean expectActivityLaunch) { 3405 final int callingUid = Binder.getCallingUid(); 3406 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3407 Log.v(TAG, "editProperties: accountType " + accountType 3408 + ", response " + response 3409 + ", expectActivityLaunch " + expectActivityLaunch 3410 + ", caller's uid " + callingUid 3411 + ", pid " + Binder.getCallingPid()); 3412 } 3413 if (response == null) throw new IllegalArgumentException("response is null"); 3414 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 3415 int userId = UserHandle.getCallingUserId(); 3416 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { 3417 String msg = String.format( 3418 "uid %s cannot edit authenticator properites for account type: %s", 3419 callingUid, 3420 accountType); 3421 throw new SecurityException(msg); 3422 } 3423 long identityToken = clearCallingIdentity(); 3424 try { 3425 UserAccounts accounts = getUserAccounts(userId); 3426 new Session(accounts, response, accountType, expectActivityLaunch, 3427 true /* stripAuthTokenFromResult */, null /* accountName */, 3428 false /* authDetailsRequired */) { 3429 @Override 3430 public void run() throws RemoteException { 3431 mAuthenticator.editProperties(this, mAccountType); 3432 } 3433 @Override 3434 protected String toDebugString(long now) { 3435 return super.toDebugString(now) + ", editProperties" 3436 + ", accountType " + accountType; 3437 } 3438 }.bind(); 3439 } finally { 3440 restoreCallingIdentity(identityToken); 3441 } 3442 } 3443 3444 @Override 3445 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName, 3446 @NonNull UserHandle userHandle) { 3447 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { 3448 throw new SecurityException("Can be called only by system UID"); 3449 } 3450 Preconditions.checkNotNull(account, "account cannot be null"); 3451 Preconditions.checkNotNull(packageName, "packageName cannot be null"); 3452 Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); 3453 3454 final int userId = userHandle.getIdentifier(); 3455 3456 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 3457 3458 try { 3459 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); 3460 return hasAccountAccess(account, packageName, uid); 3461 } catch (NameNotFoundException e) { 3462 return false; 3463 } 3464 } 3465 3466 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName, 3467 int uid) { 3468 if (packageName == null) { 3469 String[] packageNames = mPackageManager.getPackagesForUid(uid); 3470 if (ArrayUtils.isEmpty(packageNames)) { 3471 return false; 3472 } 3473 // For app op checks related to permissions all packages in the UID 3474 // have the same app op state, so doesn't matter which one we pick. 3475 packageName = packageNames[0]; 3476 } 3477 3478 // Use null token which means any token. Having a token means the package 3479 // is trusted by the authenticator, hence it is fine to access the account. 3480 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) { 3481 return true; 3482 } 3483 // In addition to the permissions required to get an auth token we also allow 3484 // the account to be accessed by holders of the get accounts permissions. 3485 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) 3486 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); 3487 } 3488 3489 private boolean checkUidPermission(String permission, int uid, String opPackageName) { 3490 final long identity = Binder.clearCallingIdentity(); 3491 try { 3492 IPackageManager pm = ActivityThread.getPackageManager(); 3493 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) { 3494 return false; 3495 } 3496 final int opCode = AppOpsManager.permissionToOpCode(permission); 3497 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( 3498 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED); 3499 } catch (RemoteException e) { 3500 /* ignore - local call */ 3501 } finally { 3502 Binder.restoreCallingIdentity(identity); 3503 } 3504 return false; 3505 } 3506 3507 @Override 3508 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account, 3509 @NonNull String packageName, @NonNull UserHandle userHandle) { 3510 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { 3511 throw new SecurityException("Can be called only by system UID"); 3512 } 3513 3514 Preconditions.checkNotNull(account, "account cannot be null"); 3515 Preconditions.checkNotNull(packageName, "packageName cannot be null"); 3516 Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); 3517 3518 final int userId = userHandle.getIdentifier(); 3519 3520 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 3521 3522 final int uid; 3523 try { 3524 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 3525 } catch (NameNotFoundException e) { 3526 Slog.e(TAG, "Unknown package " + packageName); 3527 return null; 3528 } 3529 3530 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null); 3531 3532 final long identity = Binder.clearCallingIdentity(); 3533 try { 3534 return PendingIntent.getActivityAsUser( 3535 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT 3536 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 3537 null, new UserHandle(userId)).getIntentSender(); 3538 } finally { 3539 Binder.restoreCallingIdentity(identity); 3540 } 3541 } 3542 3543 private Intent newRequestAccountAccessIntent(Account account, String packageName, 3544 int uid, RemoteCallback callback) { 3545 return newGrantCredentialsPermissionIntent(account, packageName, uid, 3546 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() { 3547 @Override 3548 public void onResult(Bundle value) throws RemoteException { 3549 handleAuthenticatorResponse(true); 3550 } 3551 3552 @Override 3553 public void onRequestContinued() { 3554 /* ignore */ 3555 } 3556 3557 @Override 3558 public void onError(int errorCode, String errorMessage) throws RemoteException { 3559 handleAuthenticatorResponse(false); 3560 } 3561 3562 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { 3563 cancelNotification(getCredentialPermissionNotificationId(account, 3564 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, 3565 UserHandle.getUserHandleForUid(uid)); 3566 if (callback != null) { 3567 Bundle result = new Bundle(); 3568 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted); 3569 callback.sendResult(result); 3570 } 3571 } 3572 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false); 3573 } 3574 3575 @Override 3576 public boolean someUserHasAccount(@NonNull final Account account) { 3577 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) { 3578 throw new SecurityException("Only system can check for accounts across users"); 3579 } 3580 final long token = Binder.clearCallingIdentity(); 3581 try { 3582 AccountAndUser[] allAccounts = getAllAccounts(); 3583 for (int i = allAccounts.length - 1; i >= 0; i--) { 3584 if (allAccounts[i].account.equals(account)) { 3585 return true; 3586 } 3587 } 3588 return false; 3589 } finally { 3590 Binder.restoreCallingIdentity(token); 3591 } 3592 } 3593 3594 private class GetAccountsByTypeAndFeatureSession extends Session { 3595 private final String[] mFeatures; 3596 private volatile Account[] mAccountsOfType = null; 3597 private volatile ArrayList<Account> mAccountsWithFeatures = null; 3598 private volatile int mCurrentAccount = 0; 3599 private final int mCallingUid; 3600 3601 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 3602 IAccountManagerResponse response, String type, String[] features, int callingUid) { 3603 super(accounts, response, type, false /* expectActivityLaunch */, 3604 true /* stripAuthTokenFromResult */, null /* accountName */, 3605 false /* authDetailsRequired */); 3606 mCallingUid = callingUid; 3607 mFeatures = features; 3608 } 3609 3610 @Override 3611 public void run() throws RemoteException { 3612 synchronized (mAccounts.cacheLock) { 3613 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 3614 null); 3615 } 3616 // check whether each account matches the requested features 3617 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 3618 mCurrentAccount = 0; 3619 3620 checkAccount(); 3621 } 3622 3623 public void checkAccount() { 3624 if (mCurrentAccount >= mAccountsOfType.length) { 3625 sendResult(); 3626 return; 3627 } 3628 3629 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 3630 if (accountAuthenticator == null) { 3631 // It is possible that the authenticator has died, which is indicated by 3632 // mAuthenticator being set to null. If this happens then just abort. 3633 // There is no need to send back a result or error in this case since 3634 // that already happened when mAuthenticator was cleared. 3635 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3636 Log.v(TAG, "checkAccount: aborting session since we are no longer" 3637 + " connected to the authenticator, " + toDebugString()); 3638 } 3639 return; 3640 } 3641 try { 3642 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 3643 } catch (RemoteException e) { 3644 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 3645 } 3646 } 3647 3648 @Override 3649 public void onResult(Bundle result) { 3650 Bundle.setDefusable(result, true); 3651 mNumResults++; 3652 if (result == null) { 3653 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 3654 return; 3655 } 3656 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 3657 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 3658 } 3659 mCurrentAccount++; 3660 checkAccount(); 3661 } 3662 3663 public void sendResult() { 3664 IAccountManagerResponse response = getResponseAndClose(); 3665 if (response != null) { 3666 try { 3667 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 3668 for (int i = 0; i < accounts.length; i++) { 3669 accounts[i] = mAccountsWithFeatures.get(i); 3670 } 3671 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3672 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 3673 + response); 3674 } 3675 Bundle result = new Bundle(); 3676 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 3677 response.onResult(result); 3678 } catch (RemoteException e) { 3679 // if the caller is dead then there is no one to care about remote exceptions 3680 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3681 Log.v(TAG, "failure while notifying response", e); 3682 } 3683 } 3684 } 3685 } 3686 3687 3688 @Override 3689 protected String toDebugString(long now) { 3690 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 3691 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 3692 } 3693 } 3694 3695 /** 3696 * Returns the accounts visible to the client within the context of a specific user 3697 * @hide 3698 */ 3699 @NonNull 3700 public Account[] getAccounts(int userId, String opPackageName) { 3701 int callingUid = Binder.getCallingUid(); 3702 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3703 opPackageName); 3704 if (visibleAccountTypes.isEmpty()) { 3705 return new Account[0]; 3706 } 3707 long identityToken = clearCallingIdentity(); 3708 try { 3709 UserAccounts accounts = getUserAccounts(userId); 3710 return getAccountsInternal( 3711 accounts, 3712 callingUid, 3713 null, // packageName 3714 visibleAccountTypes); 3715 } finally { 3716 restoreCallingIdentity(identityToken); 3717 } 3718 } 3719 3720 /** 3721 * Returns accounts for all running users. 3722 * 3723 * @hide 3724 */ 3725 @NonNull 3726 public AccountAndUser[] getRunningAccounts() { 3727 final int[] runningUserIds; 3728 try { 3729 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 3730 } catch (RemoteException e) { 3731 // Running in system_server; should never happen 3732 throw new RuntimeException(e); 3733 } 3734 return getAccounts(runningUserIds); 3735 } 3736 3737 /** {@hide} */ 3738 @NonNull 3739 public AccountAndUser[] getAllAccounts() { 3740 final List<UserInfo> users = getUserManager().getUsers(true); 3741 final int[] userIds = new int[users.size()]; 3742 for (int i = 0; i < userIds.length; i++) { 3743 userIds[i] = users.get(i).id; 3744 } 3745 return getAccounts(userIds); 3746 } 3747 3748 @NonNull 3749 private AccountAndUser[] getAccounts(int[] userIds) { 3750 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 3751 for (int userId : userIds) { 3752 UserAccounts userAccounts = getUserAccounts(userId); 3753 if (userAccounts == null) continue; 3754 synchronized (userAccounts.cacheLock) { 3755 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 3756 Binder.getCallingUid(), null); 3757 for (int a = 0; a < accounts.length; a++) { 3758 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 3759 } 3760 } 3761 } 3762 3763 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 3764 return runningAccounts.toArray(accountsArray); 3765 } 3766 3767 @Override 3768 @NonNull 3769 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { 3770 return getAccountsAsUser(type, userId, null, -1, opPackageName); 3771 } 3772 3773 @NonNull 3774 private Account[] getAccountsAsUser( 3775 String type, 3776 int userId, 3777 String callingPackage, 3778 int packageUid, 3779 String opPackageName) { 3780 int callingUid = Binder.getCallingUid(); 3781 // Only allow the system process to read accounts of other users 3782 if (userId != UserHandle.getCallingUserId() 3783 && callingUid != Process.myUid() 3784 && mContext.checkCallingOrSelfPermission( 3785 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 3786 != PackageManager.PERMISSION_GRANTED) { 3787 throw new SecurityException("User " + UserHandle.getCallingUserId() 3788 + " trying to get account for " + userId); 3789 } 3790 3791 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3792 Log.v(TAG, "getAccounts: accountType " + type 3793 + ", caller's uid " + Binder.getCallingUid() 3794 + ", pid " + Binder.getCallingPid()); 3795 } 3796 // If the original calling app was using the framework account chooser activity, we'll 3797 // be passed in the original caller's uid here, which is what should be used for filtering. 3798 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 3799 callingUid = packageUid; 3800 opPackageName = callingPackage; 3801 } 3802 3803 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3804 opPackageName); 3805 if (visibleAccountTypes.isEmpty() 3806 || (type != null && !visibleAccountTypes.contains(type))) { 3807 return new Account[0]; 3808 } else if (visibleAccountTypes.contains(type)) { 3809 // Prune the list down to just the requested type. 3810 visibleAccountTypes = new ArrayList<>(); 3811 visibleAccountTypes.add(type); 3812 } // else aggregate all the visible accounts (it won't matter if the 3813 // list is empty). 3814 3815 long identityToken = clearCallingIdentity(); 3816 try { 3817 UserAccounts accounts = getUserAccounts(userId); 3818 return getAccountsInternal( 3819 accounts, 3820 callingUid, 3821 callingPackage, 3822 visibleAccountTypes); 3823 } finally { 3824 restoreCallingIdentity(identityToken); 3825 } 3826 } 3827 3828 @NonNull 3829 private Account[] getAccountsInternal( 3830 UserAccounts userAccounts, 3831 int callingUid, 3832 String callingPackage, 3833 List<String> visibleAccountTypes) { 3834 synchronized (userAccounts.cacheLock) { 3835 ArrayList<Account> visibleAccounts = new ArrayList<>(); 3836 for (String visibleType : visibleAccountTypes) { 3837 Account[] accountsForType = getAccountsFromCacheLocked( 3838 userAccounts, visibleType, callingUid, callingPackage); 3839 if (accountsForType != null) { 3840 visibleAccounts.addAll(Arrays.asList(accountsForType)); 3841 } 3842 } 3843 Account[] result = new Account[visibleAccounts.size()]; 3844 for (int i = 0; i < visibleAccounts.size(); i++) { 3845 result[i] = visibleAccounts.get(i); 3846 } 3847 return result; 3848 } 3849 } 3850 3851 @Override 3852 public void addSharedAccountsFromParentUser(int parentUserId, int userId) { 3853 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser"); 3854 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName()); 3855 for (Account account : accounts) { 3856 addSharedAccountAsUser(account, userId); 3857 } 3858 } 3859 3860 private boolean addSharedAccountAsUser(Account account, int userId) { 3861 userId = handleIncomingUser(userId); 3862 UserAccounts accounts = getUserAccounts(userId); 3863 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3864 ContentValues values = new ContentValues(); 3865 values.put(ACCOUNTS_NAME, account.name); 3866 values.put(ACCOUNTS_TYPE, account.type); 3867 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3868 new String[] {account.name, account.type}); 3869 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 3870 if (accountId < 0) { 3871 Log.w(TAG, "insertAccountIntoDatabase: " + account 3872 + ", skipping the DB insert failed"); 3873 return false; 3874 } 3875 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts); 3876 return true; 3877 } 3878 3879 @Override 3880 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 3881 userId = handleIncomingUser(userId); 3882 UserAccounts accounts = getUserAccounts(userId); 3883 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3884 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 3885 final ContentValues values = new ContentValues(); 3886 values.put(ACCOUNTS_NAME, newName); 3887 int r = db.update( 3888 TABLE_SHARED_ACCOUNTS, 3889 values, 3890 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3891 new String[] { account.name, account.type }); 3892 if (r > 0) { 3893 int callingUid = getCallingUid(); 3894 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS, 3895 sharedTableAccountId, accounts, callingUid); 3896 // Recursively rename the account. 3897 renameAccountInternal(accounts, account, newName); 3898 } 3899 return r > 0; 3900 } 3901 3902 @Override 3903 public boolean removeSharedAccountAsUser(Account account, int userId) { 3904 return removeSharedAccountAsUser(account, userId, getCallingUid()); 3905 } 3906 3907 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) { 3908 userId = handleIncomingUser(userId); 3909 UserAccounts accounts = getUserAccounts(userId); 3910 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3911 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 3912 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 3913 new String[] {account.name, account.type}); 3914 if (r > 0) { 3915 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS, 3916 sharedTableAccountId, accounts, callingUid); 3917 removeAccountInternal(accounts, account, callingUid); 3918 } 3919 return r > 0; 3920 } 3921 3922 @Override 3923 public Account[] getSharedAccountsAsUser(int userId) { 3924 userId = handleIncomingUser(userId); 3925 UserAccounts accounts = getUserAccounts(userId); 3926 ArrayList<Account> accountList = new ArrayList<>(); 3927 Cursor cursor = null; 3928 try { 3929 cursor = accounts.openHelper.getReadableDatabase() 3930 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 3931 null, null, null, null, null); 3932 if (cursor != null && cursor.moveToFirst()) { 3933 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 3934 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 3935 do { 3936 accountList.add(new Account(cursor.getString(nameIndex), 3937 cursor.getString(typeIndex))); 3938 } while (cursor.moveToNext()); 3939 } 3940 } finally { 3941 if (cursor != null) { 3942 cursor.close(); 3943 } 3944 } 3945 Account[] accountArray = new Account[accountList.size()]; 3946 accountList.toArray(accountArray); 3947 return accountArray; 3948 } 3949 3950 @Override 3951 @NonNull 3952 public Account[] getAccounts(String type, String opPackageName) { 3953 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName); 3954 } 3955 3956 @Override 3957 @NonNull 3958 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { 3959 int callingUid = Binder.getCallingUid(); 3960 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 3961 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 3962 + callingUid + " with uid=" + uid); 3963 } 3964 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid, 3965 opPackageName); 3966 } 3967 3968 @Override 3969 @NonNull 3970 public Account[] getAccountsByTypeForPackage(String type, String packageName, 3971 String opPackageName) { 3972 int packageUid = -1; 3973 try { 3974 packageUid = AppGlobals.getPackageManager().getPackageUid( 3975 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, 3976 UserHandle.getCallingUserId()); 3977 } catch (RemoteException re) { 3978 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 3979 return new Account[0]; 3980 } 3981 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, 3982 packageUid, opPackageName); 3983 } 3984 3985 @Override 3986 public void getAccountsByFeatures( 3987 IAccountManagerResponse response, 3988 String type, 3989 String[] features, 3990 String opPackageName) { 3991 int callingUid = Binder.getCallingUid(); 3992 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3993 Log.v(TAG, "getAccounts: accountType " + type 3994 + ", response " + response 3995 + ", features " + stringArrayToString(features) 3996 + ", caller's uid " + callingUid 3997 + ", pid " + Binder.getCallingPid()); 3998 } 3999 if (response == null) throw new IllegalArgumentException("response is null"); 4000 if (type == null) throw new IllegalArgumentException("accountType is null"); 4001 int userId = UserHandle.getCallingUserId(); 4002 4003 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 4004 opPackageName); 4005 if (!visibleAccountTypes.contains(type)) { 4006 Bundle result = new Bundle(); 4007 // Need to return just the accounts that are from matching signatures. 4008 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); 4009 try { 4010 response.onResult(result); 4011 } catch (RemoteException e) { 4012 Log.e(TAG, "Cannot respond to caller do to exception." , e); 4013 } 4014 return; 4015 } 4016 long identityToken = clearCallingIdentity(); 4017 try { 4018 UserAccounts userAccounts = getUserAccounts(userId); 4019 if (features == null || features.length == 0) { 4020 Account[] accounts; 4021 synchronized (userAccounts.cacheLock) { 4022 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 4023 } 4024 Bundle result = new Bundle(); 4025 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 4026 onResult(response, result); 4027 return; 4028 } 4029 new GetAccountsByTypeAndFeatureSession( 4030 userAccounts, 4031 response, 4032 type, 4033 features, 4034 callingUid).bind(); 4035 } finally { 4036 restoreCallingIdentity(identityToken); 4037 } 4038 } 4039 4040 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) { 4041 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID}, 4042 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 4043 try { 4044 if (cursor.moveToNext()) { 4045 return cursor.getLong(0); 4046 } 4047 return -1; 4048 } finally { 4049 cursor.close(); 4050 } 4051 } 4052 4053 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 4054 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 4055 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 4056 try { 4057 if (cursor.moveToNext()) { 4058 return cursor.getLong(0); 4059 } 4060 return -1; 4061 } finally { 4062 cursor.close(); 4063 } 4064 } 4065 4066 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 4067 Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID}, 4068 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 4069 new String[]{key}, null, null, null); 4070 try { 4071 if (cursor.moveToNext()) { 4072 return cursor.getLong(0); 4073 } 4074 return -1; 4075 } finally { 4076 cursor.close(); 4077 } 4078 } 4079 4080 private abstract class Session extends IAccountAuthenticatorResponse.Stub 4081 implements IBinder.DeathRecipient, ServiceConnection { 4082 IAccountManagerResponse mResponse; 4083 final String mAccountType; 4084 final boolean mExpectActivityLaunch; 4085 final long mCreationTime; 4086 final String mAccountName; 4087 // Indicates if we need to add auth details(like last credential time) 4088 final boolean mAuthDetailsRequired; 4089 // If set, we need to update the last authenticated time. This is 4090 // currently 4091 // used on 4092 // successful confirming credentials. 4093 final boolean mUpdateLastAuthenticatedTime; 4094 4095 public int mNumResults = 0; 4096 private int mNumRequestContinued = 0; 4097 private int mNumErrors = 0; 4098 4099 IAccountAuthenticator mAuthenticator = null; 4100 4101 private final boolean mStripAuthTokenFromResult; 4102 protected final UserAccounts mAccounts; 4103 4104 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4105 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4106 boolean authDetailsRequired) { 4107 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult, 4108 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */); 4109 } 4110 4111 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4112 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4113 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) { 4114 super(); 4115 //if (response == null) throw new IllegalArgumentException("response is null"); 4116 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 4117 mAccounts = accounts; 4118 mStripAuthTokenFromResult = stripAuthTokenFromResult; 4119 mResponse = response; 4120 mAccountType = accountType; 4121 mExpectActivityLaunch = expectActivityLaunch; 4122 mCreationTime = SystemClock.elapsedRealtime(); 4123 mAccountName = accountName; 4124 mAuthDetailsRequired = authDetailsRequired; 4125 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; 4126 4127 synchronized (mSessions) { 4128 mSessions.put(toString(), this); 4129 } 4130 if (response != null) { 4131 try { 4132 response.asBinder().linkToDeath(this, 0 /* flags */); 4133 } catch (RemoteException e) { 4134 mResponse = null; 4135 binderDied(); 4136 } 4137 } 4138 } 4139 4140 IAccountManagerResponse getResponseAndClose() { 4141 if (mResponse == null) { 4142 // this session has already been closed 4143 return null; 4144 } 4145 IAccountManagerResponse response = mResponse; 4146 close(); // this clears mResponse so we need to save the response before this call 4147 return response; 4148 } 4149 4150 /** 4151 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our 4152 * security policy. 4153 * 4154 * In particular we want to make sure that the Authenticator doesn't try to trick users 4155 * into launching aribtrary intents on the device via by tricking to click authenticator 4156 * supplied entries in the system Settings app. 4157 */ 4158 protected void checkKeyIntent( 4159 int authUid, 4160 Intent intent) throws SecurityException { 4161 long bid = Binder.clearCallingIdentity(); 4162 try { 4163 PackageManager pm = mContext.getPackageManager(); 4164 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 4165 ActivityInfo targetActivityInfo = resolveInfo.activityInfo; 4166 int targetUid = targetActivityInfo.applicationInfo.uid; 4167 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) { 4168 String pkgName = targetActivityInfo.packageName; 4169 String activityName = targetActivityInfo.name; 4170 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that " 4171 + "does not share a signature with the supplying authenticator (%s)."; 4172 throw new SecurityException( 4173 String.format(tmpl, activityName, pkgName, mAccountType)); 4174 } 4175 } finally { 4176 Binder.restoreCallingIdentity(bid); 4177 } 4178 } 4179 4180 private void close() { 4181 synchronized (mSessions) { 4182 if (mSessions.remove(toString()) == null) { 4183 // the session was already closed, so bail out now 4184 return; 4185 } 4186 } 4187 if (mResponse != null) { 4188 // stop listening for response deaths 4189 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 4190 4191 // clear this so that we don't accidentally send any further results 4192 mResponse = null; 4193 } 4194 cancelTimeout(); 4195 unbind(); 4196 } 4197 4198 @Override 4199 public void binderDied() { 4200 mResponse = null; 4201 close(); 4202 } 4203 4204 protected String toDebugString() { 4205 return toDebugString(SystemClock.elapsedRealtime()); 4206 } 4207 4208 protected String toDebugString(long now) { 4209 return "Session: expectLaunch " + mExpectActivityLaunch 4210 + ", connected " + (mAuthenticator != null) 4211 + ", stats (" + mNumResults + "/" + mNumRequestContinued 4212 + "/" + mNumErrors + ")" 4213 + ", lifetime " + ((now - mCreationTime) / 1000.0); 4214 } 4215 4216 void bind() { 4217 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4218 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 4219 } 4220 if (!bindToAuthenticator(mAccountType)) { 4221 Log.d(TAG, "bind attempt failed for " + toDebugString()); 4222 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 4223 } 4224 } 4225 4226 private void unbind() { 4227 if (mAuthenticator != null) { 4228 mAuthenticator = null; 4229 mContext.unbindService(this); 4230 } 4231 } 4232 4233 public void cancelTimeout() { 4234 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 4235 } 4236 4237 @Override 4238 public void onServiceConnected(ComponentName name, IBinder service) { 4239 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 4240 try { 4241 run(); 4242 } catch (RemoteException e) { 4243 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4244 "remote exception"); 4245 } 4246 } 4247 4248 @Override 4249 public void onServiceDisconnected(ComponentName name) { 4250 mAuthenticator = null; 4251 IAccountManagerResponse response = getResponseAndClose(); 4252 if (response != null) { 4253 try { 4254 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4255 "disconnected"); 4256 } catch (RemoteException e) { 4257 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4258 Log.v(TAG, "Session.onServiceDisconnected: " 4259 + "caught RemoteException while responding", e); 4260 } 4261 } 4262 } 4263 } 4264 4265 public abstract void run() throws RemoteException; 4266 4267 public void onTimedOut() { 4268 IAccountManagerResponse response = getResponseAndClose(); 4269 if (response != null) { 4270 try { 4271 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4272 "timeout"); 4273 } catch (RemoteException e) { 4274 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4275 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 4276 e); 4277 } 4278 } 4279 } 4280 } 4281 4282 @Override 4283 public void onResult(Bundle result) { 4284 Bundle.setDefusable(result, true); 4285 mNumResults++; 4286 Intent intent = null; 4287 if (result != null) { 4288 boolean isSuccessfulConfirmCreds = result.getBoolean( 4289 AccountManager.KEY_BOOLEAN_RESULT, false); 4290 boolean isSuccessfulUpdateCredsOrAddAccount = 4291 result.containsKey(AccountManager.KEY_ACCOUNT_NAME) 4292 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE); 4293 // We should only update lastAuthenticated time, if 4294 // mUpdateLastAuthenticatedTime is true and the confirmRequest 4295 // or updateRequest was successful 4296 boolean needUpdate = mUpdateLastAuthenticatedTime 4297 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount); 4298 if (needUpdate || mAuthDetailsRequired) { 4299 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType); 4300 if (needUpdate && accountPresent) { 4301 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType)); 4302 } 4303 if (mAuthDetailsRequired) { 4304 long lastAuthenticatedTime = -1; 4305 if (accountPresent) { 4306 lastAuthenticatedTime = DatabaseUtils.longForQuery( 4307 mAccounts.openHelper.getReadableDatabase(), 4308 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4309 + " FROM " + 4310 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND " 4311 + ACCOUNTS_TYPE + "=?", 4312 new String[] { 4313 mAccountName, mAccountType 4314 }); 4315 } 4316 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME, 4317 lastAuthenticatedTime); 4318 } 4319 } 4320 } 4321 if (result != null 4322 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 4323 checkKeyIntent( 4324 Binder.getCallingUid(), 4325 intent); 4326 } 4327 if (result != null 4328 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 4329 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 4330 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 4331 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 4332 Account account = new Account(accountName, accountType); 4333 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 4334 new UserHandle(mAccounts.userId)); 4335 } 4336 } 4337 IAccountManagerResponse response; 4338 if (mExpectActivityLaunch && result != null 4339 && result.containsKey(AccountManager.KEY_INTENT)) { 4340 response = mResponse; 4341 } else { 4342 response = getResponseAndClose(); 4343 } 4344 if (response != null) { 4345 try { 4346 if (result == null) { 4347 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4348 Log.v(TAG, getClass().getSimpleName() 4349 + " calling onError() on response " + response); 4350 } 4351 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 4352 "null bundle returned"); 4353 } else { 4354 if (mStripAuthTokenFromResult) { 4355 result.remove(AccountManager.KEY_AUTHTOKEN); 4356 } 4357 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4358 Log.v(TAG, getClass().getSimpleName() 4359 + " calling onResult() on response " + response); 4360 } 4361 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 4362 (intent == null)) { 4363 // All AccountManager error codes are greater than 0 4364 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 4365 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 4366 } else { 4367 response.onResult(result); 4368 } 4369 } 4370 } catch (RemoteException e) { 4371 // if the caller is dead then there is no one to care about remote exceptions 4372 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4373 Log.v(TAG, "failure while notifying response", e); 4374 } 4375 } 4376 } 4377 } 4378 4379 @Override 4380 public void onRequestContinued() { 4381 mNumRequestContinued++; 4382 } 4383 4384 @Override 4385 public void onError(int errorCode, String errorMessage) { 4386 mNumErrors++; 4387 IAccountManagerResponse response = getResponseAndClose(); 4388 if (response != null) { 4389 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4390 Log.v(TAG, getClass().getSimpleName() 4391 + " calling onError() on response " + response); 4392 } 4393 try { 4394 response.onError(errorCode, errorMessage); 4395 } catch (RemoteException e) { 4396 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4397 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 4398 } 4399 } 4400 } else { 4401 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4402 Log.v(TAG, "Session.onError: already closed"); 4403 } 4404 } 4405 } 4406 4407 /** 4408 * find the component name for the authenticator and initiate a bind 4409 * if no authenticator or the bind fails then return false, otherwise return true 4410 */ 4411 private boolean bindToAuthenticator(String authenticatorType) { 4412 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 4413 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 4414 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 4415 if (authenticatorInfo == null) { 4416 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4417 Log.v(TAG, "there is no authenticator for " + authenticatorType 4418 + ", bailing out"); 4419 } 4420 return false; 4421 } 4422 4423 if (!isLocalUnlockedUser(mAccounts.userId) 4424 && !authenticatorInfo.componentInfo.directBootAware) { 4425 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName 4426 + " which isn't encryption aware"); 4427 return false; 4428 } 4429 4430 Intent intent = new Intent(); 4431 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 4432 intent.setComponent(authenticatorInfo.componentName); 4433 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4434 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 4435 } 4436 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 4437 UserHandle.of(mAccounts.userId))) { 4438 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4439 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 4440 } 4441 return false; 4442 } 4443 4444 return true; 4445 } 4446 } 4447 4448 class MessageHandler extends Handler { 4449 MessageHandler(Looper looper) { 4450 super(looper); 4451 } 4452 4453 @Override 4454 public void handleMessage(Message msg) { 4455 switch (msg.what) { 4456 case MESSAGE_TIMED_OUT: 4457 Session session = (Session)msg.obj; 4458 session.onTimedOut(); 4459 break; 4460 4461 case MESSAGE_COPY_SHARED_ACCOUNT: 4462 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); 4463 break; 4464 4465 default: 4466 throw new IllegalStateException("unhandled message: " + msg.what); 4467 } 4468 } 4469 } 4470 4471 @VisibleForTesting 4472 String getPreNDatabaseName(int userId) { 4473 File systemDir = Environment.getDataSystemDirectory(); 4474 File databaseFile = new File(Environment.getUserSystemDirectory(userId), 4475 PRE_N_DATABASE_NAME); 4476 if (userId == 0) { 4477 // Migrate old file, if it exists, to the new location. 4478 // Make sure the new file doesn't already exist. A dummy file could have been 4479 // accidentally created in the old location, causing the new one to become corrupted 4480 // as well. 4481 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME); 4482 if (oldFile.exists() && !databaseFile.exists()) { 4483 // Check for use directory; create if it doesn't exist, else renameTo will fail 4484 File userDir = Environment.getUserSystemDirectory(userId); 4485 if (!userDir.exists()) { 4486 if (!userDir.mkdirs()) { 4487 throw new IllegalStateException("User dir cannot be created: " + userDir); 4488 } 4489 } 4490 if (!oldFile.renameTo(databaseFile)) { 4491 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 4492 } 4493 } 4494 } 4495 return databaseFile.getPath(); 4496 } 4497 4498 @VisibleForTesting 4499 String getDeDatabaseName(int userId) { 4500 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId), 4501 DE_DATABASE_NAME); 4502 return databaseFile.getPath(); 4503 } 4504 4505 @VisibleForTesting 4506 String getCeDatabaseName(int userId) { 4507 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId), 4508 CE_DATABASE_NAME); 4509 return databaseFile.getPath(); 4510 } 4511 4512 private static class DebugDbHelper{ 4513 private DebugDbHelper() { 4514 } 4515 4516 private static String TABLE_DEBUG = "debug_table"; 4517 4518 // Columns for the table 4519 private static String ACTION_TYPE = "action_type"; 4520 private static String TIMESTAMP = "time"; 4521 private static String CALLER_UID = "caller_uid"; 4522 private static String TABLE_NAME = "table_name"; 4523 private static String KEY = "primary_key"; 4524 4525 // These actions correspond to the occurrence of real actions. Since 4526 // these are called by the authenticators, the uid associated will be 4527 // of the authenticator. 4528 private static String ACTION_SET_PASSWORD = "action_set_password"; 4529 private static String ACTION_CLEAR_PASSWORD = "action_clear_password"; 4530 private static String ACTION_ACCOUNT_ADD = "action_account_add"; 4531 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove"; 4532 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de"; 4533 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove"; 4534 private static String ACTION_ACCOUNT_RENAME = "action_account_rename"; 4535 4536 // These actions don't necessarily correspond to any action on 4537 // accountDb taking place. As an example, there might be a request for 4538 // addingAccount, which might not lead to addition of account on grounds 4539 // of bad authentication. We will still be logging it to keep track of 4540 // who called. 4541 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add"; 4542 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove"; 4543 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts"; 4544 4545 //This action doesn't add account to accountdb. Account is only 4546 // added in finishSession which may be in a different user profile. 4547 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add"; 4548 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH = 4549 "action_called_account_session_finish"; 4550 4551 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 4552 4553 private static void createDebugTable(SQLiteDatabase db) { 4554 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " 4555 + ACCOUNTS_ID + " INTEGER," 4556 + ACTION_TYPE + " TEXT NOT NULL, " 4557 + TIMESTAMP + " DATETIME," 4558 + CALLER_UID + " INTEGER NOT NULL," 4559 + TABLE_NAME + " TEXT NOT NULL," 4560 + KEY + " INTEGER PRIMARY KEY)"); 4561 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")"); 4562 } 4563 } 4564 4565 private void logRecord(UserAccounts accounts, String action, String tableName) { 4566 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4567 logRecord(db, action, tableName, -1, accounts); 4568 } 4569 4570 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) { 4571 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4572 logRecord(db, action, tableName, -1, accounts, uid); 4573 } 4574 4575 /* 4576 * This function receives an opened writable database. 4577 */ 4578 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 4579 UserAccounts userAccount) { 4580 logRecord(db, action, tableName, accountId, userAccount, getCallingUid()); 4581 } 4582 4583 /* 4584 * This function receives an opened writable database. 4585 */ 4586 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 4587 UserAccounts userAccount, int callingUid) { 4588 SQLiteStatement logStatement = userAccount.statementForLogging; 4589 logStatement.bindLong(1, accountId); 4590 logStatement.bindString(2, action); 4591 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date())); 4592 logStatement.bindLong(4, callingUid); 4593 logStatement.bindString(5, tableName); 4594 logStatement.bindLong(6, userAccount.debugDbInsertionPoint); 4595 logStatement.execute(); 4596 logStatement.clearBindings(); 4597 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1) 4598 % MAX_DEBUG_DB_SIZE; 4599 } 4600 4601 /* 4602 * This should only be called once to compile the sql statement for logging 4603 * and to find the insertion point. 4604 */ 4605 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db, 4606 UserAccounts userAccount) { 4607 // Initialize the count if not done earlier. 4608 int size = (int) getDebugTableRowCount(db); 4609 if (size >= MAX_DEBUG_DB_SIZE) { 4610 // Table is full, and we need to find the point where to insert. 4611 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db); 4612 } else { 4613 userAccount.debugDbInsertionPoint = size; 4614 } 4615 compileSqlStatementForLogging(db, userAccount); 4616 } 4617 4618 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) { 4619 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG 4620 + " VALUES (?,?,?,?,?,?)"; 4621 userAccount.statementForLogging = db.compileStatement(sql); 4622 } 4623 4624 private long getDebugTableRowCount(SQLiteDatabase db) { 4625 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG; 4626 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 4627 } 4628 4629 /* 4630 * Finds the row key where the next insertion should take place. This should 4631 * be invoked only if the table has reached its full capacity. 4632 */ 4633 private long getDebugTableInsertionPoint(SQLiteDatabase db) { 4634 // This query finds the smallest timestamp value (and if 2 records have 4635 // same timestamp, the choose the lower id). 4636 String queryCountDebugDbRows = new StringBuilder() 4637 .append("SELECT ").append(DebugDbHelper.KEY) 4638 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG) 4639 .append(" ORDER BY ") 4640 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY) 4641 .append(" LIMIT 1") 4642 .toString(); 4643 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 4644 } 4645 4646 static class PreNDatabaseHelper extends SQLiteOpenHelper { 4647 private final Context mContext; 4648 private final int mUserId; 4649 4650 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) { 4651 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION); 4652 mContext = context; 4653 mUserId = userId; 4654 } 4655 4656 @Override 4657 public void onCreate(SQLiteDatabase db) { 4658 // We use PreNDatabaseHelper only if pre-N db exists 4659 throw new IllegalStateException("Legacy database cannot be created - only upgraded!"); 4660 } 4661 4662 private void createSharedAccountsTable(SQLiteDatabase db) { 4663 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 4664 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4665 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4666 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4667 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4668 } 4669 4670 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) { 4671 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " 4672 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0"); 4673 } 4674 4675 private void addOldAccountNameColumn(SQLiteDatabase db) { 4676 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 4677 } 4678 4679 private void addDebugTable(SQLiteDatabase db) { 4680 DebugDbHelper.createDebugTable(db); 4681 } 4682 4683 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4684 db.execSQL("" 4685 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4686 + " BEGIN" 4687 + " DELETE FROM " + TABLE_AUTHTOKENS 4688 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4689 + " DELETE FROM " + TABLE_EXTRAS 4690 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4691 + " DELETE FROM " + TABLE_GRANTS 4692 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4693 + " END"); 4694 } 4695 4696 private void createGrantsTable(SQLiteDatabase db) { 4697 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 4698 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4699 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 4700 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 4701 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 4702 + "," + GRANTS_GRANTEE_UID + "))"); 4703 } 4704 4705 private void populateMetaTableWithAuthTypeAndUID( 4706 SQLiteDatabase db, 4707 Map<String, Integer> authTypeAndUIDMap) { 4708 Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator(); 4709 while (iterator.hasNext()) { 4710 Entry<String, Integer> entry = iterator.next(); 4711 ContentValues values = new ContentValues(); 4712 values.put(META_KEY, 4713 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey()); 4714 values.put(META_VALUE, entry.getValue()); 4715 db.insert(TABLE_META, null, values); 4716 } 4717 } 4718 4719 /** 4720 * Pre-N database may need an upgrade before splitting 4721 */ 4722 @Override 4723 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 4724 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 4725 4726 if (oldVersion == 1) { 4727 // no longer need to do anything since the work is done 4728 // when upgrading from version 2 4729 oldVersion++; 4730 } 4731 4732 if (oldVersion == 2) { 4733 createGrantsTable(db); 4734 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 4735 createAccountsDeletionTrigger(db); 4736 oldVersion++; 4737 } 4738 4739 if (oldVersion == 3) { 4740 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 4741 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 4742 oldVersion++; 4743 } 4744 4745 if (oldVersion == 4) { 4746 createSharedAccountsTable(db); 4747 oldVersion++; 4748 } 4749 4750 if (oldVersion == 5) { 4751 addOldAccountNameColumn(db); 4752 oldVersion++; 4753 } 4754 4755 if (oldVersion == 6) { 4756 addLastSuccessfullAuthenticatedTimeColumn(db); 4757 oldVersion++; 4758 } 4759 4760 if (oldVersion == 7) { 4761 addDebugTable(db); 4762 oldVersion++; 4763 } 4764 4765 if (oldVersion == 8) { 4766 populateMetaTableWithAuthTypeAndUID( 4767 db, 4768 AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId)); 4769 oldVersion++; 4770 } 4771 4772 if (oldVersion != newVersion) { 4773 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 4774 } 4775 } 4776 4777 @Override 4778 public void onOpen(SQLiteDatabase db) { 4779 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 4780 } 4781 } 4782 4783 static class DeDatabaseHelper extends SQLiteOpenHelper { 4784 4785 private final int mUserId; 4786 private volatile boolean mCeAttached; 4787 4788 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) { 4789 super(context, deDatabaseName, null, DE_DATABASE_VERSION); 4790 mUserId = userId; 4791 } 4792 4793 /** 4794 * This call needs to be made while the mCacheLock is held. The way to 4795 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 4796 * @param db The database. 4797 */ 4798 @Override 4799 public void onCreate(SQLiteDatabase db) { 4800 Log.i(TAG, "Creating DE database for user " + mUserId); 4801 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 4802 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, " 4803 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4804 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4805 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 4806 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, " 4807 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4808 4809 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 4810 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 4811 + META_VALUE + " TEXT)"); 4812 4813 createGrantsTable(db); 4814 createSharedAccountsTable(db); 4815 createAccountsDeletionTrigger(db); 4816 DebugDbHelper.createDebugTable(db); 4817 } 4818 4819 private void createSharedAccountsTable(SQLiteDatabase db) { 4820 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 4821 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4822 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4823 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4824 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4825 } 4826 4827 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4828 db.execSQL("" 4829 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4830 + " BEGIN" 4831 + " DELETE FROM " + TABLE_GRANTS 4832 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4833 + " END"); 4834 } 4835 4836 private void createGrantsTable(SQLiteDatabase db) { 4837 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 4838 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4839 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 4840 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 4841 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 4842 + "," + GRANTS_GRANTEE_UID + "))"); 4843 } 4844 4845 @Override 4846 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 4847 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 4848 4849 if (oldVersion != newVersion) { 4850 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 4851 } 4852 } 4853 4854 public void attachCeDatabase(File ceDbFile) { 4855 SQLiteDatabase db = getWritableDatabase(); 4856 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb"); 4857 mCeAttached = true; 4858 } 4859 4860 public boolean isCeDatabaseAttached() { 4861 return mCeAttached; 4862 } 4863 4864 4865 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() { 4866 if(!mCeAttached) { 4867 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId 4868 + " is still locked. CE database is not yet available.", new Throwable()); 4869 } 4870 return super.getReadableDatabase(); 4871 } 4872 4873 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() { 4874 if(!mCeAttached) { 4875 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId 4876 + " is still locked. CE database is not yet available.", new Throwable()); 4877 } 4878 return super.getWritableDatabase(); 4879 } 4880 4881 @Override 4882 public void onOpen(SQLiteDatabase db) { 4883 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME); 4884 } 4885 4886 private void migratePreNDbToDe(File preNDbFile) { 4887 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile); 4888 SQLiteDatabase db = getWritableDatabase(); 4889 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb"); 4890 db.beginTransaction(); 4891 // Copy accounts fields 4892 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS 4893 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 4894 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4895 + ") " 4896 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 4897 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 4898 + " FROM preNDb." + TABLE_ACCOUNTS); 4899 // Copy SHARED_ACCOUNTS 4900 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS 4901 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " + 4902 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 4903 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS); 4904 // Copy DEBUG_TABLE 4905 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG 4906 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + "," 4907 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + "," 4908 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " + 4909 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + "," 4910 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + "," 4911 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY 4912 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG); 4913 // Copy GRANTS 4914 db.execSQL("INSERT INTO " + TABLE_GRANTS 4915 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 4916 + GRANTS_GRANTEE_UID + ") " + 4917 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 4918 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS); 4919 // Copy META 4920 db.execSQL("INSERT INTO " + TABLE_META 4921 + "(" + META_KEY + "," + META_VALUE + ") " 4922 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META); 4923 db.setTransactionSuccessful(); 4924 db.endTransaction(); 4925 4926 db.execSQL("DETACH DATABASE preNDb"); 4927 } 4928 4929 static DeDatabaseHelper create( 4930 Context context, 4931 int userId, 4932 File preNDatabaseFile, 4933 File deDatabaseFile) { 4934 boolean newDbExists = deDatabaseFile.exists(); 4935 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId, 4936 deDatabaseFile.getPath()); 4937 // If the db just created, and there is a legacy db, migrate it 4938 if (!newDbExists && preNDatabaseFile.exists()) { 4939 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION 4940 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId, 4941 preNDatabaseFile.getPath()); 4942 // Open the database to force upgrade if required 4943 preNDatabaseHelper.getWritableDatabase(); 4944 preNDatabaseHelper.close(); 4945 // Move data without SPII to DE 4946 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile); 4947 } 4948 return deDatabaseHelper; 4949 } 4950 } 4951 4952 static class CeDatabaseHelper extends SQLiteOpenHelper { 4953 4954 public CeDatabaseHelper(Context context, String ceDatabaseName) { 4955 super(context, ceDatabaseName, null, CE_DATABASE_VERSION); 4956 } 4957 4958 /** 4959 * This call needs to be made while the mCacheLock is held. 4960 * @param db The database. 4961 */ 4962 @Override 4963 public void onCreate(SQLiteDatabase db) { 4964 Log.i(TAG, "Creating CE database " + getDatabaseName()); 4965 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 4966 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4967 + ACCOUNTS_NAME + " TEXT NOT NULL, " 4968 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 4969 + ACCOUNTS_PASSWORD + " TEXT, " 4970 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 4971 4972 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 4973 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4974 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 4975 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 4976 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 4977 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 4978 4979 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 4980 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 4981 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 4982 + EXTRAS_KEY + " TEXT NOT NULL, " 4983 + EXTRAS_VALUE + " TEXT, " 4984 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 4985 4986 createAccountsDeletionTrigger(db); 4987 } 4988 4989 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 4990 db.execSQL("" 4991 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 4992 + " BEGIN" 4993 + " DELETE FROM " + TABLE_AUTHTOKENS 4994 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4995 + " DELETE FROM " + TABLE_EXTRAS 4996 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 4997 + " END"); 4998 } 4999 5000 @Override 5001 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 5002 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion); 5003 5004 if (oldVersion == 9) { 5005 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5006 Log.v(TAG, "onUpgrade upgrading to v10"); 5007 } 5008 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META); 5009 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS); 5010 // Recreate the trigger, since the old one references the table to be removed 5011 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete"); 5012 createAccountsDeletionTrigger(db); 5013 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS); 5014 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG); 5015 oldVersion ++; 5016 } 5017 5018 if (oldVersion != newVersion) { 5019 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 5020 } 5021 } 5022 5023 @Override 5024 public void onOpen(SQLiteDatabase db) { 5025 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME); 5026 } 5027 5028 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name, 5029 String type) { 5030 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, 5031 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 5032 new String[]{name, type}, null, null, null); 5033 try { 5034 if (cursor.moveToNext()) { 5035 return cursor.getString(0); 5036 } 5037 return null; 5038 } finally { 5039 cursor.close(); 5040 } 5041 } 5042 5043 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) { 5044 // Select accounts from CE that do not exist in DE 5045 Cursor cursor = db.rawQuery( 5046 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 5047 + " FROM " + CE_TABLE_ACCOUNTS 5048 + " WHERE NOT EXISTS " 5049 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS 5050 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 5051 + " )", null); 5052 try { 5053 List<Account> accounts = new ArrayList<>(cursor.getCount()); 5054 while (cursor.moveToNext()) { 5055 String accountName = cursor.getString(0); 5056 String accountType = cursor.getString(1); 5057 accounts.add(new Account(accountName, accountType)); 5058 } 5059 return accounts; 5060 } finally { 5061 cursor.close(); 5062 } 5063 } 5064 5065 /** 5066 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location, 5067 * it also performs migration to the new CE database. 5068 * @param context 5069 * @param userId id of the user where the database is located 5070 */ 5071 static CeDatabaseHelper create( 5072 Context context, 5073 int userId, 5074 File preNDatabaseFile, 5075 File ceDatabaseFile) { 5076 boolean newDbExists = ceDatabaseFile.exists(); 5077 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5078 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists=" 5079 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists); 5080 } 5081 boolean removeOldDb = false; 5082 if (!newDbExists && preNDatabaseFile.exists()) { 5083 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile); 5084 } 5085 // Try to open and upgrade if necessary 5086 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath()); 5087 ceHelper.getWritableDatabase(); 5088 ceHelper.close(); 5089 if (removeOldDb) { 5090 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile); 5091 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) { 5092 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile); 5093 } 5094 } 5095 return ceHelper; 5096 } 5097 5098 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) { 5099 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile); 5100 try { 5101 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile); 5102 } catch (IOException e) { 5103 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e); 5104 // Try to remove potentially damaged file if I/O error occurred 5105 deleteDbFileWarnIfFailed(ceDbFile); 5106 return false; 5107 } 5108 return true; 5109 } 5110 } 5111 5112 public IBinder onBind(@SuppressWarnings("unused") Intent intent) { 5113 return asBinder(); 5114 } 5115 5116 /** 5117 * Searches array of arguments for the specified string 5118 * @param args array of argument strings 5119 * @param value value to search for 5120 * @return true if the value is contained in the array 5121 */ 5122 private static boolean scanArgs(String[] args, String value) { 5123 if (args != null) { 5124 for (String arg : args) { 5125 if (value.equals(arg)) { 5126 return true; 5127 } 5128 } 5129 } 5130 return false; 5131 } 5132 5133 @Override 5134 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 5135 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 5136 != PackageManager.PERMISSION_GRANTED) { 5137 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 5138 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 5139 + " without permission " + android.Manifest.permission.DUMP); 5140 return; 5141 } 5142 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 5143 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 5144 5145 final List<UserInfo> users = getUserManager().getUsers(); 5146 for (UserInfo user : users) { 5147 ipw.println("User " + user + ":"); 5148 ipw.increaseIndent(); 5149 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 5150 ipw.println(); 5151 ipw.decreaseIndent(); 5152 } 5153 } 5154 5155 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 5156 String[] args, boolean isCheckinRequest) { 5157 synchronized (userAccounts.cacheLock) { 5158 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 5159 5160 if (isCheckinRequest) { 5161 // This is a checkin request. *Only* upload the account types and the count of each. 5162 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 5163 null, null, ACCOUNTS_TYPE, null, null); 5164 try { 5165 while (cursor.moveToNext()) { 5166 // print type,count 5167 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 5168 } 5169 } finally { 5170 if (cursor != null) { 5171 cursor.close(); 5172 } 5173 } 5174 } else { 5175 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 5176 Process.myUid(), null); 5177 fout.println("Accounts: " + accounts.length); 5178 for (Account account : accounts) { 5179 fout.println(" " + account); 5180 } 5181 5182 // Add debug information. 5183 fout.println(); 5184 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null, 5185 null, null, null, null, DebugDbHelper.TIMESTAMP); 5186 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key"); 5187 fout.println("Accounts History"); 5188 try { 5189 while (cursor.moveToNext()) { 5190 // print type,count 5191 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," + 5192 cursor.getString(2) + "," + cursor.getString(3) + "," 5193 + cursor.getString(4) + "," + cursor.getString(5)); 5194 } 5195 } finally { 5196 cursor.close(); 5197 } 5198 5199 fout.println(); 5200 synchronized (mSessions) { 5201 final long now = SystemClock.elapsedRealtime(); 5202 fout.println("Active Sessions: " + mSessions.size()); 5203 for (Session session : mSessions.values()) { 5204 fout.println(" " + session.toDebugString(now)); 5205 } 5206 } 5207 5208 fout.println(); 5209 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 5210 } 5211 } 5212 } 5213 5214 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 5215 Intent intent, String packageName, final int userId) { 5216 long identityToken = clearCallingIdentity(); 5217 try { 5218 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5219 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 5220 } 5221 5222 if (intent.getComponent() != null && 5223 GrantCredentialsPermissionActivity.class.getName().equals( 5224 intent.getComponent().getClassName())) { 5225 createNoCredentialsPermissionNotification(account, intent, packageName, userId); 5226 } else { 5227 Context contextForUser = getContextForUser(new UserHandle(userId)); 5228 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 5229 intent.addCategory(String.valueOf(notificationId)); 5230 5231 final String notificationTitleFormat = 5232 contextForUser.getText(R.string.notification_title).toString(); 5233 Notification n = new Notification.Builder(contextForUser) 5234 .setWhen(0) 5235 .setSmallIcon(android.R.drawable.stat_sys_warning) 5236 .setColor(contextForUser.getColor( 5237 com.android.internal.R.color.system_notification_accent_color)) 5238 .setContentTitle(String.format(notificationTitleFormat, account.name)) 5239 .setContentText(message) 5240 .setContentIntent(PendingIntent.getActivityAsUser( 5241 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 5242 null, new UserHandle(userId))) 5243 .build(); 5244 installNotification(notificationId, n, packageName, userId); 5245 } 5246 } finally { 5247 restoreCallingIdentity(identityToken); 5248 } 5249 } 5250 5251 @VisibleForTesting 5252 protected void installNotification(int notificationId, final Notification notification, 5253 UserHandle user) { 5254 installNotification(notificationId, notification, "android", user.getIdentifier()); 5255 } 5256 5257 private void installNotification(int notificationId, final Notification notification, 5258 String packageName, int userId) { 5259 final long token = clearCallingIdentity(); 5260 try { 5261 INotificationManager notificationManager = NotificationManager.getService(); 5262 try { 5263 notificationManager.enqueueNotificationWithTag(packageName, packageName, null, 5264 notificationId, notification, new int[1], userId); 5265 } catch (RemoteException e) { 5266 /* ignore - local call */ 5267 } 5268 } finally { 5269 Binder.restoreCallingIdentity(token); 5270 } 5271 } 5272 5273 @VisibleForTesting 5274 protected void cancelNotification(int id, UserHandle user) { 5275 cancelNotification(id, mContext.getPackageName(), user); 5276 } 5277 5278 protected void cancelNotification(int id, String packageName, UserHandle user) { 5279 long identityToken = clearCallingIdentity(); 5280 try { 5281 INotificationManager service = INotificationManager.Stub.asInterface( 5282 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 5283 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier()); 5284 } catch (RemoteException e) { 5285 /* ignore - local call */ 5286 } finally { 5287 restoreCallingIdentity(identityToken); 5288 } 5289 } 5290 5291 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { 5292 for (String perm : permissions) { 5293 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 5294 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5295 Log.v(TAG, " caller uid " + callingUid + " has " + perm); 5296 } 5297 final int opCode = AppOpsManager.permissionToOpCode(perm); 5298 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( 5299 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { 5300 return true; 5301 } 5302 } 5303 } 5304 return false; 5305 } 5306 5307 private int handleIncomingUser(int userId) { 5308 try { 5309 return ActivityManagerNative.getDefault().handleIncomingUser( 5310 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 5311 } catch (RemoteException re) { 5312 // Shouldn't happen, local. 5313 } 5314 return userId; 5315 } 5316 5317 private boolean isPrivileged(int callingUid) { 5318 final int callingUserId = UserHandle.getUserId(callingUid); 5319 5320 final PackageManager userPackageManager; 5321 try { 5322 userPackageManager = mContext.createPackageContextAsUser( 5323 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 5324 } catch (NameNotFoundException e) { 5325 return false; 5326 } 5327 5328 String[] packages = userPackageManager.getPackagesForUid(callingUid); 5329 for (String name : packages) { 5330 try { 5331 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 5332 if (packageInfo != null 5333 && (packageInfo.applicationInfo.privateFlags 5334 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 5335 return true; 5336 } 5337 } catch (PackageManager.NameNotFoundException e) { 5338 return false; 5339 } 5340 } 5341 return false; 5342 } 5343 5344 private boolean permissionIsGranted( 5345 Account account, String authTokenType, int callerUid, int userId) { 5346 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { 5347 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5348 Log.v(TAG, "Access to " + account + " granted calling uid is system"); 5349 } 5350 return true; 5351 } 5352 5353 if (isPrivileged(callerUid)) { 5354 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5355 Log.v(TAG, "Access to " + account + " granted calling uid " 5356 + callerUid + " privileged"); 5357 } 5358 return true; 5359 } 5360 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) { 5361 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5362 Log.v(TAG, "Access to " + account + " granted calling uid " 5363 + callerUid + " manages the account"); 5364 } 5365 return true; 5366 } 5367 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) { 5368 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5369 Log.v(TAG, "Access to " + account + " granted calling uid " 5370 + callerUid + " user granted access"); 5371 } 5372 return true; 5373 } 5374 5375 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5376 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid); 5377 } 5378 5379 return false; 5380 } 5381 5382 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, 5383 String opPackageName) { 5384 if (accountType == null) { 5385 return false; 5386 } else { 5387 return getTypesVisibleToCaller(callingUid, userId, 5388 opPackageName).contains(accountType); 5389 } 5390 } 5391 5392 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { 5393 if (accountType == null) { 5394 return false; 5395 } else { 5396 return getTypesManagedByCaller(callingUid, userId).contains(accountType); 5397 } 5398 } 5399 5400 private List<String> getTypesVisibleToCaller(int callingUid, int userId, 5401 String opPackageName) { 5402 boolean isPermitted = 5403 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, 5404 Manifest.permission.GET_ACCOUNTS_PRIVILEGED); 5405 return getTypesForCaller(callingUid, userId, isPermitted); 5406 } 5407 5408 private List<String> getTypesManagedByCaller(int callingUid, int userId) { 5409 return getTypesForCaller(callingUid, userId, false); 5410 } 5411 5412 private List<String> getTypesForCaller( 5413 int callingUid, int userId, boolean isOtherwisePermitted) { 5414 List<String> managedAccountTypes = new ArrayList<>(); 5415 long identityToken = Binder.clearCallingIdentity(); 5416 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 5417 try { 5418 serviceInfos = mAuthenticatorCache.getAllServices(userId); 5419 } finally { 5420 Binder.restoreCallingIdentity(identityToken); 5421 } 5422 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 5423 serviceInfos) { 5424 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); 5425 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { 5426 managedAccountTypes.add(serviceInfo.type.type); 5427 } 5428 } 5429 return managedAccountTypes; 5430 } 5431 5432 private boolean isAccountPresentForCaller(String accountName, String accountType) { 5433 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) { 5434 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) { 5435 if (account.name.equals(accountName)) { 5436 return true; 5437 } 5438 } 5439 } 5440 return false; 5441 } 5442 5443 private static void checkManageUsersPermission(String message) { 5444 if (ActivityManager.checkComponentPermission( 5445 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true) 5446 != PackageManager.PERMISSION_GRANTED) { 5447 throw new SecurityException("You need MANAGE_USERS permission to: " + message); 5448 } 5449 } 5450 5451 private static void checkManageOrCreateUsersPermission(String message) { 5452 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS, 5453 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED && 5454 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS, 5455 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) { 5456 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: " 5457 + message); 5458 } 5459 } 5460 5461 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 5462 int callerUid) { 5463 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { 5464 return true; 5465 } 5466 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid)); 5467 synchronized (accounts.cacheLock) { 5468 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 5469 5470 final String query; 5471 final String[] args; 5472 5473 if (authTokenType != null) { 5474 query = COUNT_OF_MATCHING_GRANTS; 5475 args = new String[] {String.valueOf(callerUid), authTokenType, 5476 account.name, account.type}; 5477 } else { 5478 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN; 5479 args = new String[] {String.valueOf(callerUid), account.name, 5480 account.type}; 5481 } 5482 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0; 5483 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 5484 // TODO: Skip this check when running automated tests. Replace this 5485 // with a more general solution. 5486 Log.d(TAG, "no credentials permission for usage of " + account + ", " 5487 + authTokenType + " by uid " + callerUid 5488 + " but ignoring since device is in test harness."); 5489 return true; 5490 } 5491 return permissionGranted; 5492 } 5493 } 5494 5495 private boolean isSystemUid(int callingUid) { 5496 String[] packages = null; 5497 long ident = Binder.clearCallingIdentity(); 5498 try { 5499 packages = mPackageManager.getPackagesForUid(callingUid); 5500 } finally { 5501 Binder.restoreCallingIdentity(ident); 5502 } 5503 if (packages != null) { 5504 for (String name : packages) { 5505 try { 5506 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); 5507 if (packageInfo != null 5508 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 5509 != 0) { 5510 return true; 5511 } 5512 } catch (PackageManager.NameNotFoundException e) { 5513 Log.w(TAG, String.format("Could not find package [%s]", name), e); 5514 } 5515 } 5516 } else { 5517 Log.w(TAG, "No known packages with uid " + callingUid); 5518 } 5519 return false; 5520 } 5521 5522 /** Succeeds if any of the specified permissions are granted. */ 5523 private void checkReadAccountsPermitted( 5524 int callingUid, 5525 String accountType, 5526 int userId, 5527 String opPackageName) { 5528 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { 5529 String msg = String.format( 5530 "caller uid %s cannot access %s accounts", 5531 callingUid, 5532 accountType); 5533 Log.w(TAG, " " + msg); 5534 throw new SecurityException(msg); 5535 } 5536 } 5537 5538 private boolean canUserModifyAccounts(int userId, int callingUid) { 5539 // the managing app can always modify accounts 5540 if (isProfileOwner(callingUid)) { 5541 return true; 5542 } 5543 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 5544 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 5545 return false; 5546 } 5547 return true; 5548 } 5549 5550 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) { 5551 // the managing app can always modify accounts 5552 if (isProfileOwner(callingUid)) { 5553 return true; 5554 } 5555 DevicePolicyManager dpm = (DevicePolicyManager) mContext 5556 .getSystemService(Context.DEVICE_POLICY_SERVICE); 5557 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 5558 if (typesArray == null) { 5559 return true; 5560 } 5561 for (String forbiddenType : typesArray) { 5562 if (forbiddenType.equals(accountType)) { 5563 return false; 5564 } 5565 } 5566 return true; 5567 } 5568 5569 private boolean isProfileOwner(int uid) { 5570 final DevicePolicyManagerInternal dpmi = 5571 LocalServices.getService(DevicePolicyManagerInternal.class); 5572 return (dpmi != null) 5573 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); 5574 } 5575 5576 @Override 5577 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 5578 throws RemoteException { 5579 final int callingUid = getCallingUid(); 5580 5581 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { 5582 throw new SecurityException(); 5583 } 5584 5585 if (value) { 5586 grantAppPermission(account, authTokenType, uid); 5587 } else { 5588 revokeAppPermission(account, authTokenType, uid); 5589 } 5590 } 5591 5592 /** 5593 * Allow callers with the given uid permission to get credentials for account/authTokenType. 5594 * <p> 5595 * Although this is public it can only be accessed via the AccountManagerService object 5596 * which is in the system. This means we don't need to protect it with permissions. 5597 * @hide 5598 */ 5599 void grantAppPermission(Account account, String authTokenType, int uid) { 5600 if (account == null || authTokenType == null) { 5601 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 5602 return; 5603 } 5604 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5605 synchronized (accounts.cacheLock) { 5606 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 5607 db.beginTransaction(); 5608 try { 5609 long accountId = getAccountIdLocked(db, account); 5610 if (accountId >= 0) { 5611 ContentValues values = new ContentValues(); 5612 values.put(GRANTS_ACCOUNTS_ID, accountId); 5613 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 5614 values.put(GRANTS_GRANTEE_UID, uid); 5615 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 5616 db.setTransactionSuccessful(); 5617 } 5618 } finally { 5619 db.endTransaction(); 5620 } 5621 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 5622 UserHandle.of(accounts.userId)); 5623 5624 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); 5625 } 5626 5627 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 5628 for (AccountManagerInternal.OnAppPermissionChangeListener listener 5629 : mAppPermissionChangeListeners) { 5630 mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid)); 5631 } 5632 } 5633 5634 /** 5635 * Don't allow callers with the given uid permission to get credentials for 5636 * account/authTokenType. 5637 * <p> 5638 * Although this is public it can only be accessed via the AccountManagerService object 5639 * which is in the system. This means we don't need to protect it with permissions. 5640 * @hide 5641 */ 5642 private void revokeAppPermission(Account account, String authTokenType, int uid) { 5643 if (account == null || authTokenType == null) { 5644 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 5645 return; 5646 } 5647 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5648 synchronized (accounts.cacheLock) { 5649 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 5650 db.beginTransaction(); 5651 try { 5652 long accountId = getAccountIdLocked(db, account); 5653 if (accountId >= 0) { 5654 db.delete(TABLE_GRANTS, 5655 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 5656 + GRANTS_GRANTEE_UID + "=?", 5657 new String[]{String.valueOf(accountId), authTokenType, 5658 String.valueOf(uid)}); 5659 db.setTransactionSuccessful(); 5660 } 5661 } finally { 5662 db.endTransaction(); 5663 } 5664 5665 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 5666 new UserHandle(accounts.userId)); 5667 } 5668 5669 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 5670 for (AccountManagerInternal.OnAppPermissionChangeListener listener 5671 : mAppPermissionChangeListeners) { 5672 mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid)); 5673 } 5674 } 5675 5676 static final private String stringArrayToString(String[] value) { 5677 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 5678 } 5679 5680 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 5681 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 5682 if (oldAccountsForType != null) { 5683 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 5684 for (Account curAccount : oldAccountsForType) { 5685 if (!curAccount.equals(account)) { 5686 newAccountsList.add(curAccount); 5687 } 5688 } 5689 if (newAccountsList.isEmpty()) { 5690 accounts.accountCache.remove(account.type); 5691 } else { 5692 Account[] newAccountsForType = new Account[newAccountsList.size()]; 5693 newAccountsForType = newAccountsList.toArray(newAccountsForType); 5694 accounts.accountCache.put(account.type, newAccountsForType); 5695 } 5696 } 5697 accounts.userDataCache.remove(account); 5698 accounts.authTokenCache.remove(account); 5699 accounts.previousNameCache.remove(account); 5700 } 5701 5702 /** 5703 * This assumes that the caller has already checked that the account is not already present. 5704 */ 5705 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 5706 Account[] accountsForType = accounts.accountCache.get(account.type); 5707 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 5708 Account[] newAccountsForType = new Account[oldLength + 1]; 5709 if (accountsForType != null) { 5710 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 5711 } 5712 newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker()); 5713 accounts.accountCache.put(account.type, newAccountsForType); 5714 } 5715 5716 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 5717 int callingUid, String callingPackage) { 5718 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 5719 || callingUid == Process.myUid()) { 5720 return unfiltered; 5721 } 5722 UserInfo user = getUserManager().getUserInfo(userAccounts.userId); 5723 if (user != null && user.isRestricted()) { 5724 String[] packages = mPackageManager.getPackagesForUid(callingUid); 5725 // If any of the packages is a white listed package, return the full set, 5726 // otherwise return non-shared accounts only. 5727 // This might be a temporary way to specify a whitelist 5728 String whiteList = mContext.getResources().getString( 5729 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 5730 for (String packageName : packages) { 5731 if (whiteList.contains(";" + packageName + ";")) { 5732 return unfiltered; 5733 } 5734 } 5735 ArrayList<Account> allowed = new ArrayList<Account>(); 5736 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 5737 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 5738 String requiredAccountType = ""; 5739 try { 5740 // If there's an explicit callingPackage specified, check if that package 5741 // opted in to see restricted accounts. 5742 if (callingPackage != null) { 5743 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 5744 if (pi != null && pi.restrictedAccountType != null) { 5745 requiredAccountType = pi.restrictedAccountType; 5746 } 5747 } else { 5748 // Otherwise check if the callingUid has a package that has opted in 5749 for (String packageName : packages) { 5750 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 5751 if (pi != null && pi.restrictedAccountType != null) { 5752 requiredAccountType = pi.restrictedAccountType; 5753 break; 5754 } 5755 } 5756 } 5757 } catch (NameNotFoundException nnfe) { 5758 } 5759 for (Account account : unfiltered) { 5760 if (account.type.equals(requiredAccountType)) { 5761 allowed.add(account); 5762 } else { 5763 boolean found = false; 5764 for (Account shared : sharedAccounts) { 5765 if (shared.equals(account)) { 5766 found = true; 5767 break; 5768 } 5769 } 5770 if (!found) { 5771 allowed.add(account); 5772 } 5773 } 5774 } 5775 Account[] filtered = new Account[allowed.size()]; 5776 allowed.toArray(filtered); 5777 return filtered; 5778 } else { 5779 return unfiltered; 5780 } 5781 } 5782 5783 /* 5784 * packageName can be null. If not null, it should be used to filter out restricted accounts 5785 * that the package is not allowed to access. 5786 */ 5787 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 5788 int callingUid, String callingPackage) { 5789 if (accountType != null) { 5790 final Account[] accounts = userAccounts.accountCache.get(accountType); 5791 if (accounts == null) { 5792 return EMPTY_ACCOUNT_ARRAY; 5793 } else { 5794 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 5795 callingUid, callingPackage); 5796 } 5797 } else { 5798 int totalLength = 0; 5799 for (Account[] accounts : userAccounts.accountCache.values()) { 5800 totalLength += accounts.length; 5801 } 5802 if (totalLength == 0) { 5803 return EMPTY_ACCOUNT_ARRAY; 5804 } 5805 Account[] accounts = new Account[totalLength]; 5806 totalLength = 0; 5807 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 5808 System.arraycopy(accountsOfType, 0, accounts, totalLength, 5809 accountsOfType.length); 5810 totalLength += accountsOfType.length; 5811 } 5812 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 5813 } 5814 } 5815 5816 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 5817 Account account, String key, String value) { 5818 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 5819 if (userDataForAccount == null) { 5820 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 5821 accounts.userDataCache.put(account, userDataForAccount); 5822 } 5823 if (value == null) { 5824 userDataForAccount.remove(key); 5825 } else { 5826 userDataForAccount.put(key, value); 5827 } 5828 } 5829 5830 protected String readCachedTokenInternal( 5831 UserAccounts accounts, 5832 Account account, 5833 String tokenType, 5834 String callingPackage, 5835 byte[] pkgSigDigest) { 5836 synchronized (accounts.cacheLock) { 5837 return accounts.accountTokenCaches.get( 5838 account, tokenType, callingPackage, pkgSigDigest); 5839 } 5840 } 5841 5842 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 5843 Account account, String key, String value) { 5844 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 5845 if (authTokensForAccount == null) { 5846 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 5847 accounts.authTokenCache.put(account, authTokensForAccount); 5848 } 5849 if (value == null) { 5850 authTokensForAccount.remove(key); 5851 } else { 5852 authTokensForAccount.put(key, value); 5853 } 5854 } 5855 5856 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 5857 String authTokenType) { 5858 synchronized (accounts.cacheLock) { 5859 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 5860 if (authTokensForAccount == null) { 5861 // need to populate the cache for this account 5862 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 5863 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 5864 accounts.authTokenCache.put(account, authTokensForAccount); 5865 } 5866 return authTokensForAccount.get(authTokenType); 5867 } 5868 } 5869 5870 protected String readUserDataInternalLocked( 5871 UserAccounts accounts, Account account, String key) { 5872 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 5873 if (userDataForAccount == null) { 5874 // need to populate the cache for this account 5875 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); 5876 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 5877 accounts.userDataCache.put(account, userDataForAccount); 5878 } 5879 return userDataForAccount.get(key); 5880 } 5881 5882 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 5883 final SQLiteDatabase db, Account account) { 5884 HashMap<String, String> userDataForAccount = new HashMap<>(); 5885 Cursor cursor = db.query(CE_TABLE_EXTRAS, 5886 COLUMNS_EXTRAS_KEY_AND_VALUE, 5887 SELECTION_USERDATA_BY_ACCOUNT, 5888 new String[]{account.name, account.type}, 5889 null, null, null); 5890 try { 5891 while (cursor.moveToNext()) { 5892 final String tmpkey = cursor.getString(0); 5893 final String value = cursor.getString(1); 5894 userDataForAccount.put(tmpkey, value); 5895 } 5896 } finally { 5897 cursor.close(); 5898 } 5899 return userDataForAccount; 5900 } 5901 5902 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 5903 final SQLiteDatabase db, Account account) { 5904 HashMap<String, String> authTokensForAccount = new HashMap<>(); 5905 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS, 5906 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 5907 SELECTION_AUTHTOKENS_BY_ACCOUNT, 5908 new String[]{account.name, account.type}, 5909 null, null, null); 5910 try { 5911 while (cursor.moveToNext()) { 5912 final String type = cursor.getString(0); 5913 final String authToken = cursor.getString(1); 5914 authTokensForAccount.put(type, authToken); 5915 } 5916 } finally { 5917 cursor.close(); 5918 } 5919 return authTokensForAccount; 5920 } 5921 5922 private Context getContextForUser(UserHandle user) { 5923 try { 5924 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 5925 } catch (NameNotFoundException e) { 5926 // Default to mContext, not finding the package system is running as is unlikely. 5927 return mContext; 5928 } 5929 } 5930 5931 private void sendResponse(IAccountManagerResponse response, Bundle result) { 5932 try { 5933 response.onResult(result); 5934 } catch (RemoteException e) { 5935 // if the caller is dead then there is no one to care about remote 5936 // exceptions 5937 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5938 Log.v(TAG, "failure while notifying response", e); 5939 } 5940 } 5941 } 5942 5943 private void sendErrorResponse(IAccountManagerResponse response, int errorCode, 5944 String errorMessage) { 5945 try { 5946 response.onError(errorCode, errorMessage); 5947 } catch (RemoteException e) { 5948 // if the caller is dead then there is no one to care about remote 5949 // exceptions 5950 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5951 Log.v(TAG, "failure while notifying response", e); 5952 } 5953 } 5954 } 5955 5956 private final class AccountAccessTracker extends IAccountAccessTracker.Stub { 5957 @Override 5958 public void onAccountAccessed() throws RemoteException { 5959 final int uid = Binder.getCallingUid(); 5960 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { 5961 return; 5962 } 5963 final int userId = UserHandle.getCallingUserId(); 5964 final long identity = Binder.clearCallingIdentity(); 5965 try { 5966 for (Account account : getAccounts(userId, mContext.getOpPackageName())) { 5967 IAccountAccessTracker accountTracker = account.getAccessTracker(); 5968 if (accountTracker != null && asBinder() == accountTracker.asBinder()) { 5969 // An app just accessed the account. At this point it knows about 5970 // it and there is not need to hide this account from the app. 5971 if (!hasAccountAccess(account, null, uid)) { 5972 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, 5973 uid, true); 5974 } 5975 } 5976 } 5977 } finally { 5978 Binder.restoreCallingIdentity(identity); 5979 } 5980 } 5981 } 5982 5983 private final class AccountManagerInternalImpl extends AccountManagerInternal { 5984 private final Object mLock = new Object(); 5985 5986 @GuardedBy("mLock") 5987 private AccountManagerBackupHelper mBackupHelper; 5988 5989 @Override 5990 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, 5991 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { 5992 if (account == null) { 5993 Slog.w(TAG, "account cannot be null"); 5994 return; 5995 } 5996 if (packageName == null) { 5997 Slog.w(TAG, "packageName cannot be null"); 5998 return; 5999 } 6000 if (userId < UserHandle.USER_SYSTEM) { 6001 Slog.w(TAG, "user id must be concrete"); 6002 return; 6003 } 6004 if (callback == null) { 6005 Slog.w(TAG, "callback cannot be null"); 6006 return; 6007 } 6008 6009 if (AccountManagerService.this.hasAccountAccess(account, packageName, 6010 new UserHandle(userId))) { 6011 Bundle result = new Bundle(); 6012 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 6013 callback.sendResult(result); 6014 return; 6015 } 6016 6017 final int uid; 6018 try { 6019 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 6020 } catch (NameNotFoundException e) { 6021 Slog.e(TAG, "Unknown package " + packageName); 6022 return; 6023 } 6024 6025 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); 6026 final UserAccounts userAccounts; 6027 synchronized (mUsers) { 6028 userAccounts = mUsers.get(userId); 6029 } 6030 doNotification(userAccounts, account, null, intent, packageName, userId); 6031 } 6032 6033 @Override 6034 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) { 6035 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 6036 mAppPermissionChangeListeners.add(listener); 6037 } 6038 6039 @Override 6040 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) { 6041 return AccountManagerService.this.hasAccountAccess(account, null, uid); 6042 } 6043 6044 @Override 6045 public byte[] backupAccountAccessPermissions(int userId) { 6046 synchronized (mLock) { 6047 if (mBackupHelper == null) { 6048 mBackupHelper = new AccountManagerBackupHelper( 6049 AccountManagerService.this, this); 6050 } 6051 return mBackupHelper.backupAccountAccessPermissions(userId); 6052 } 6053 } 6054 6055 @Override 6056 public void restoreAccountAccessPermissions(byte[] data, int userId) { 6057 synchronized (mLock) { 6058 if (mBackupHelper == null) { 6059 mBackupHelper = new AccountManagerBackupHelper( 6060 AccountManagerService.this, this); 6061 } 6062 mBackupHelper.restoreAccountAccessPermissions(data, userId); 6063 } 6064 } 6065 } 6066} 6067