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