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 long accountId = accounts.accountsDb.insertCeAccount(account, password); 1837 if (accountId < 0) { 1838 Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() 1839 + ", skipping the DB insert failed"); 1840 return false; 1841 } 1842 // Insert into DE table 1843 if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) { 1844 Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() 1845 + ", skipping the DB insert failed"); 1846 return false; 1847 } 1848 if (extras != null) { 1849 for (String key : extras.keySet()) { 1850 final String value = extras.getString(key); 1851 if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) { 1852 Log.w(TAG, "insertAccountIntoDatabase: " 1853 + account.toSafeString() 1854 + ", skipping since insertExtra failed for key " + key); 1855 return false; 1856 } else { 1857 AccountManager.invalidateLocalAccountUserDataCaches(); 1858 } 1859 } 1860 } 1861 1862 if (packageToVisibility != null) { 1863 for (Entry<String, Integer> entry : packageToVisibility.entrySet()) { 1864 setAccountVisibility(account, entry.getKey() /* package */, 1865 entry.getValue() /* visibility */, false /* notify */, 1866 accounts); 1867 } 1868 } 1869 accounts.accountsDb.setTransactionSuccessful(); 1870 1871 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, 1872 accountId, 1873 accounts, callingUid); 1874 1875 insertAccountIntoCacheLocked(accounts, account); 1876 } finally { 1877 accounts.accountsDb.endTransaction(); 1878 } 1879 } 1880 } 1881 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) { 1882 addAccountToLinkedRestrictedUsers(account, accounts.userId); 1883 } 1884 1885 sendNotificationAccountUpdated(account, accounts); 1886 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed. 1887 sendAccountsChangedBroadcast(accounts.userId); 1888 1889 logAddAccountExplicitlyMetrics(opPackageName, account.type, packageToVisibility); 1890 return true; 1891 } 1892 logAddAccountExplicitlyMetrics( String callerPackage, String accountType, @Nullable Map<String, Integer> accountVisibility)1893 private void logAddAccountExplicitlyMetrics( 1894 String callerPackage, String accountType, 1895 @Nullable Map<String, Integer> accountVisibility) { 1896 // Although this is not a 'device policy' API, enterprise is the current use case. 1897 DevicePolicyEventLogger 1898 .createEvent(DevicePolicyEnums.ADD_ACCOUNT_EXPLICITLY) 1899 .setStrings( 1900 TextUtils.emptyIfNull(accountType), 1901 TextUtils.emptyIfNull(callerPackage), 1902 findPackagesPerVisibility(accountVisibility)) 1903 .write(); 1904 } 1905 findPackagesPerVisibility(@ullable Map<String, Integer> accountVisibility)1906 private String[] findPackagesPerVisibility(@Nullable Map<String, Integer> accountVisibility) { 1907 Map<Integer, Set<String>> packagesPerVisibility = new HashMap<>(); 1908 if (accountVisibility != null) { 1909 for (Entry<String, Integer> entry : accountVisibility.entrySet()) { 1910 if (!packagesPerVisibility.containsKey(entry.getValue())) { 1911 packagesPerVisibility.put(entry.getValue(), new HashSet<>()); 1912 } 1913 packagesPerVisibility.get(entry.getValue()).add(entry.getKey()); 1914 } 1915 } 1916 1917 String[] packagesPerVisibilityStr = new String[5]; 1918 packagesPerVisibilityStr[AccountManager.VISIBILITY_UNDEFINED] = getPackagesForVisibilityStr( 1919 AccountManager.VISIBILITY_UNDEFINED, packagesPerVisibility); 1920 packagesPerVisibilityStr[AccountManager.VISIBILITY_VISIBLE] = getPackagesForVisibilityStr( 1921 AccountManager.VISIBILITY_VISIBLE, packagesPerVisibility); 1922 packagesPerVisibilityStr[AccountManager.VISIBILITY_USER_MANAGED_VISIBLE] = 1923 getPackagesForVisibilityStr( 1924 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE, packagesPerVisibility); 1925 packagesPerVisibilityStr[AccountManager.VISIBILITY_NOT_VISIBLE] = 1926 getPackagesForVisibilityStr( 1927 AccountManager.VISIBILITY_NOT_VISIBLE, packagesPerVisibility); 1928 packagesPerVisibilityStr[AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE] = 1929 getPackagesForVisibilityStr( 1930 AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE, packagesPerVisibility); 1931 return packagesPerVisibilityStr; 1932 } 1933 getPackagesForVisibilityStr( int visibility, Map<Integer, Set<String>> packagesPerVisibility)1934 private String getPackagesForVisibilityStr( 1935 int visibility, Map<Integer, Set<String>> packagesPerVisibility) { 1936 return visibility + ":" 1937 + (packagesPerVisibility.containsKey(visibility) 1938 ? TextUtils.join(",", packagesPerVisibility.get(visibility)) 1939 : ""); 1940 } 1941 isLocalUnlockedUser(int userId)1942 private boolean isLocalUnlockedUser(int userId) { 1943 synchronized (mUsers) { 1944 return mLocalUnlockedUsers.get(userId); 1945 } 1946 } 1947 1948 /** 1949 * Adds the account to all linked restricted users as shared accounts. If the user is currently 1950 * running, then clone the account too. 1951 * @param account the account to share with limited users 1952 * 1953 */ addAccountToLinkedRestrictedUsers(Account account, int parentUserId)1954 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) { 1955 List<UserInfo> users = getUserManager().getUsers(); 1956 for (UserInfo user : users) { 1957 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) { 1958 addSharedAccountAsUser(account, user.id); 1959 if (isLocalUnlockedUser(user.id)) { 1960 mHandler.sendMessage(mHandler.obtainMessage( 1961 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account)); 1962 } 1963 } 1964 } 1965 } 1966 1967 @Override hasFeatures(IAccountManagerResponse response, Account account, String[] features, String opPackageName)1968 public void hasFeatures(IAccountManagerResponse response, 1969 Account account, String[] features, String opPackageName) { 1970 int callingUid = Binder.getCallingUid(); 1971 mAppOpsManager.checkPackage(callingUid, opPackageName); 1972 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1973 Log.v(TAG, "hasFeatures: " + account 1974 + ", response " + response 1975 + ", features " + Arrays.toString(features) 1976 + ", caller's uid " + callingUid 1977 + ", pid " + Binder.getCallingPid()); 1978 } 1979 Preconditions.checkArgument(account != null, "account cannot be null"); 1980 Preconditions.checkArgument(response != null, "response cannot be null"); 1981 Preconditions.checkArgument(features != null, "features cannot be null"); 1982 int userId = UserHandle.getCallingUserId(); 1983 checkReadAccountsPermitted(callingUid, account.type, userId, 1984 opPackageName); 1985 1986 final long identityToken = clearCallingIdentity(); 1987 try { 1988 UserAccounts accounts = getUserAccounts(userId); 1989 new TestFeaturesSession(accounts, response, account, features).bind(); 1990 } finally { 1991 restoreCallingIdentity(identityToken); 1992 } 1993 } 1994 1995 private class TestFeaturesSession extends Session { 1996 private final String[] mFeatures; 1997 private final Account mAccount; 1998 TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, Account account, String[] features)1999 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, 2000 Account account, String[] features) { 2001 super(accounts, response, account.type, false /* expectActivityLaunch */, 2002 true /* stripAuthTokenFromResult */, account.name, 2003 false /* authDetailsRequired */); 2004 mFeatures = features; 2005 mAccount = account; 2006 } 2007 2008 @Override run()2009 public void run() throws RemoteException { 2010 try { 2011 mAuthenticator.hasFeatures(this, mAccount, mFeatures); 2012 } catch (RemoteException e) { 2013 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 2014 } 2015 } 2016 2017 @Override onResult(Bundle result)2018 public void onResult(Bundle result) { 2019 Bundle.setDefusable(result, true); 2020 IAccountManagerResponse response = getResponseAndClose(); 2021 if (response != null) { 2022 try { 2023 if (result == null) { 2024 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 2025 return; 2026 } 2027 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2028 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2029 + response); 2030 } 2031 final Bundle newResult = new Bundle(); 2032 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 2033 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 2034 response.onResult(newResult); 2035 } catch (RemoteException e) { 2036 // if the caller is dead then there is no one to care about remote exceptions 2037 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2038 Log.v(TAG, "failure while notifying response", e); 2039 } 2040 } 2041 } 2042 } 2043 2044 @Override toDebugString(long now)2045 protected String toDebugString(long now) { 2046 return super.toDebugString(now) + ", hasFeatures" 2047 + ", " + mAccount 2048 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 2049 } 2050 } 2051 2052 @Override renameAccount( IAccountManagerResponse response, Account accountToRename, String newName)2053 public void renameAccount( 2054 IAccountManagerResponse response, Account accountToRename, String newName) { 2055 final int callingUid = Binder.getCallingUid(); 2056 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2057 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName 2058 + ", caller's uid " + callingUid 2059 + ", pid " + Binder.getCallingPid()); 2060 } 2061 if (accountToRename == null) throw new IllegalArgumentException("account is null"); 2062 int userId = UserHandle.getCallingUserId(); 2063 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { 2064 String msg = String.format( 2065 "uid %s cannot rename accounts of type: %s", 2066 callingUid, 2067 accountToRename.type); 2068 throw new SecurityException(msg); 2069 } 2070 final long identityToken = clearCallingIdentity(); 2071 try { 2072 UserAccounts accounts = getUserAccounts(userId); 2073 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName); 2074 Bundle result = new Bundle(); 2075 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); 2076 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); 2077 result.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID, 2078 resultingAccount.getAccessId()); 2079 try { 2080 response.onResult(result); 2081 } catch (RemoteException e) { 2082 Log.w(TAG, e.getMessage()); 2083 } 2084 } finally { 2085 restoreCallingIdentity(identityToken); 2086 } 2087 } 2088 renameAccountInternal( UserAccounts accounts, Account accountToRename, String newName)2089 private Account renameAccountInternal( 2090 UserAccounts accounts, Account accountToRename, String newName) { 2091 Account resultAccount = null; 2092 /* 2093 * Cancel existing notifications. Let authenticators 2094 * re-post notifications as required. But we don't know if 2095 * the authenticators have bound their notifications to 2096 * now stale account name data. 2097 * 2098 * With a rename api, we might not need to do this anymore but it 2099 * shouldn't hurt. 2100 */ 2101 cancelNotification( 2102 getSigninRequiredNotificationId(accounts, accountToRename), 2103 new UserHandle(accounts.userId)); 2104 synchronized(accounts.credentialsPermissionNotificationIds) { 2105 for (Pair<Pair<Account, String>, Integer> pair: 2106 accounts.credentialsPermissionNotificationIds.keySet()) { 2107 if (accountToRename.equals(pair.first.first)) { 2108 NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair); 2109 cancelNotification(id, new UserHandle(accounts.userId)); 2110 } 2111 } 2112 } 2113 synchronized (accounts.dbLock) { 2114 synchronized (accounts.cacheLock) { 2115 List<String> accountRemovedReceivers = 2116 getAccountRemovedReceivers(accountToRename, accounts); 2117 accounts.accountsDb.beginTransaction(); 2118 Account renamedAccount = new Account(newName, accountToRename.type); 2119 try { 2120 if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) { 2121 Log.e(TAG, "renameAccount failed - account with new name already exists"); 2122 return null; 2123 } 2124 final long accountId = accounts.accountsDb.findDeAccountId(accountToRename); 2125 if (accountId >= 0) { 2126 accounts.accountsDb.renameCeAccount(accountId, newName); 2127 if (accounts.accountsDb.renameDeAccount( 2128 accountId, newName, accountToRename.name)) { 2129 accounts.accountsDb.setTransactionSuccessful(); 2130 } else { 2131 Log.e(TAG, "renameAccount failed"); 2132 return null; 2133 } 2134 } else { 2135 Log.e(TAG, "renameAccount failed - old account does not exist"); 2136 return null; 2137 } 2138 } finally { 2139 accounts.accountsDb.endTransaction(); 2140 } 2141 /* 2142 * Database transaction was successful. Clean up cached 2143 * data associated with the account in the user profile. 2144 */ 2145 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount); 2146 /* 2147 * Extract the data and token caches before removing the 2148 * old account to preserve the user data associated with 2149 * the account. 2150 */ 2151 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename); 2152 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename); 2153 Map<String, Integer> tmpVisibility = accounts.visibilityCache.get(accountToRename); 2154 removeAccountFromCacheLocked(accounts, accountToRename); 2155 /* 2156 * Update the cached data associated with the renamed 2157 * account. 2158 */ 2159 accounts.userDataCache.put(renamedAccount, tmpData); 2160 accounts.authTokenCache.put(renamedAccount, tmpTokens); 2161 accounts.visibilityCache.put(renamedAccount, tmpVisibility); 2162 accounts.previousNameCache.put( 2163 renamedAccount, 2164 new AtomicReference<>(accountToRename.name)); 2165 resultAccount = renamedAccount; 2166 2167 int parentUserId = accounts.userId; 2168 if (canHaveProfile(parentUserId)) { 2169 /* 2170 * Owner or system user account was renamed, rename the account for 2171 * those users with which the account was shared. 2172 */ 2173 List<UserInfo> users = getUserManager().getAliveUsers(); 2174 for (UserInfo user : users) { 2175 if (user.isRestricted() 2176 && (user.restrictedProfileParentId == parentUserId)) { 2177 renameSharedAccountAsUser(accountToRename, newName, user.id); 2178 } 2179 } 2180 } 2181 2182 sendNotificationAccountUpdated(resultAccount, accounts); 2183 sendAccountsChangedBroadcast(accounts.userId); 2184 for (String packageName : accountRemovedReceivers) { 2185 sendAccountRemovedBroadcast(accountToRename, packageName, accounts.userId); 2186 } 2187 2188 AccountManager.invalidateLocalAccountsDataCaches(); 2189 AccountManager.invalidateLocalAccountUserDataCaches(); 2190 } 2191 } 2192 return resultAccount; 2193 } 2194 canHaveProfile(final int parentUserId)2195 private boolean canHaveProfile(final int parentUserId) { 2196 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId); 2197 return userInfo != null && userInfo.canHaveProfile(); 2198 } 2199 2200 @Override removeAccountAsUser(IAccountManagerResponse response, Account account, boolean expectActivityLaunch, int userId)2201 public void removeAccountAsUser(IAccountManagerResponse response, Account account, 2202 boolean expectActivityLaunch, int userId) { 2203 final int callingUid = Binder.getCallingUid(); 2204 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2205 Log.v(TAG, "removeAccount: " + account 2206 + ", response " + response 2207 + ", caller's uid " + callingUid 2208 + ", pid " + Binder.getCallingPid() 2209 + ", for user id " + userId); 2210 } 2211 Preconditions.checkArgument(account != null, "account cannot be null"); 2212 Preconditions.checkArgument(response != null, "response cannot be null"); 2213 2214 // Only allow the system process to modify accounts of other users 2215 if (isCrossUser(callingUid, userId)) { 2216 throw new SecurityException( 2217 String.format( 2218 "User %s tying remove account for %s" , 2219 UserHandle.getCallingUserId(), 2220 userId)); 2221 } 2222 /* 2223 * Only the system, authenticator or profile owner should be allowed to remove accounts for 2224 * that authenticator. This will let users remove accounts (via Settings in the system) but 2225 * not arbitrary applications (like competing authenticators). 2226 */ 2227 UserHandle user = UserHandle.of(userId); 2228 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) 2229 && !isSystemUid(callingUid) 2230 && !isProfileOwner(callingUid)) { 2231 String msg = String.format( 2232 "uid %s cannot remove accounts of type: %s", 2233 callingUid, 2234 account.type); 2235 throw new SecurityException(msg); 2236 } 2237 if (!canUserModifyAccounts(userId, callingUid)) { 2238 try { 2239 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2240 "User cannot modify accounts"); 2241 } catch (RemoteException re) { 2242 } 2243 return; 2244 } 2245 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) { 2246 try { 2247 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2248 "User cannot modify accounts of this type (policy)."); 2249 } catch (RemoteException re) { 2250 } 2251 return; 2252 } 2253 final long identityToken = clearCallingIdentity(); 2254 UserAccounts accounts = getUserAccounts(userId); 2255 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 2256 synchronized(accounts.credentialsPermissionNotificationIds) { 2257 for (Pair<Pair<Account, String>, Integer> pair: 2258 accounts.credentialsPermissionNotificationIds.keySet()) { 2259 if (account.equals(pair.first.first)) { 2260 NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair); 2261 cancelNotification(id, user); 2262 } 2263 } 2264 } 2265 final long accountId = accounts.accountsDb.findDeAccountId(account); 2266 logRecord( 2267 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE, 2268 AccountsDb.TABLE_ACCOUNTS, 2269 accountId, 2270 accounts, 2271 callingUid); 2272 try { 2273 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); 2274 } finally { 2275 restoreCallingIdentity(identityToken); 2276 } 2277 } 2278 2279 @Override removeAccountExplicitly(Account account)2280 public boolean removeAccountExplicitly(Account account) { 2281 final int callingUid = Binder.getCallingUid(); 2282 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2283 Log.v(TAG, "removeAccountExplicitly: " + account 2284 + ", caller's uid " + callingUid 2285 + ", pid " + Binder.getCallingPid()); 2286 } 2287 int userId = Binder.getCallingUserHandle().getIdentifier(); 2288 if (account == null) { 2289 /* 2290 * Null accounts should result in returning false, as per 2291 * AccountManage.addAccountExplicitly(...) java doc. 2292 */ 2293 Log.e(TAG, "account is null"); 2294 return false; 2295 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2296 String msg = String.format( 2297 "uid %s cannot explicitly remove accounts of type: %s", 2298 callingUid, 2299 account.type); 2300 throw new SecurityException(msg); 2301 } 2302 UserAccounts accounts = getUserAccountsForCaller(); 2303 final long accountId = accounts.accountsDb.findDeAccountId(account); 2304 logRecord( 2305 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE, 2306 AccountsDb.TABLE_ACCOUNTS, 2307 accountId, 2308 accounts, 2309 callingUid); 2310 final long identityToken = clearCallingIdentity(); 2311 try { 2312 return removeAccountInternal(accounts, account, callingUid); 2313 } finally { 2314 restoreCallingIdentity(identityToken); 2315 } 2316 } 2317 2318 private class RemoveAccountSession extends Session { 2319 final Account mAccount; RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, Account account, boolean expectActivityLaunch)2320 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, 2321 Account account, boolean expectActivityLaunch) { 2322 super(accounts, response, account.type, expectActivityLaunch, 2323 true /* stripAuthTokenFromResult */, account.name, 2324 false /* authDetailsRequired */); 2325 mAccount = account; 2326 } 2327 2328 @Override toDebugString(long now)2329 protected String toDebugString(long now) { 2330 return super.toDebugString(now) + ", removeAccount" 2331 + ", account " + mAccount; 2332 } 2333 2334 @Override run()2335 public void run() throws RemoteException { 2336 mAuthenticator.getAccountRemovalAllowed(this, mAccount); 2337 } 2338 2339 @Override onResult(Bundle result)2340 public void onResult(Bundle result) { 2341 Bundle.setDefusable(result, true); 2342 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) 2343 && !result.containsKey(AccountManager.KEY_INTENT)) { 2344 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); 2345 if (removalAllowed) { 2346 removeAccountInternal(mAccounts, mAccount, getCallingUid()); 2347 } 2348 IAccountManagerResponse response = getResponseAndClose(); 2349 if (response != null) { 2350 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2351 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2352 + response); 2353 } 2354 try { 2355 response.onResult(result); 2356 } catch (RemoteException e) { 2357 Slog.e(TAG, "Error calling onResult()", e); 2358 } 2359 } 2360 } 2361 super.onResult(result); 2362 } 2363 } 2364 2365 @VisibleForTesting removeAccountInternal(Account account)2366 protected void removeAccountInternal(Account account) { 2367 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid()); 2368 } 2369 removeAccountInternal(UserAccounts accounts, Account account, int callingUid)2370 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) { 2371 boolean isChanged = false; 2372 boolean userUnlocked = isLocalUnlockedUser(accounts.userId); 2373 if (!userUnlocked) { 2374 Slog.i(TAG, "Removing account " + account.toSafeString() 2375 + " while user " + accounts.userId 2376 + " is still locked. CE data will be removed later"); 2377 } 2378 synchronized (accounts.dbLock) { 2379 synchronized (accounts.cacheLock) { 2380 Map<String, Integer> packagesToVisibility = getRequestingPackages(account, 2381 accounts); 2382 List<String> accountRemovedReceivers = 2383 getAccountRemovedReceivers(account, accounts); 2384 accounts.accountsDb.beginTransaction(); 2385 // Set to a placeholder value, this will only be used if the database 2386 // transaction succeeds. 2387 long accountId = -1; 2388 try { 2389 accountId = accounts.accountsDb.findDeAccountId(account); 2390 if (accountId >= 0) { 2391 isChanged = accounts.accountsDb.deleteDeAccount(accountId); 2392 } 2393 // always delete from CE table if CE storage is available 2394 // DE account could be removed while CE was locked 2395 if (userUnlocked) { 2396 long ceAccountId = accounts.accountsDb.findCeAccountId(account); 2397 if (ceAccountId >= 0) { 2398 accounts.accountsDb.deleteCeAccount(ceAccountId); 2399 } 2400 } 2401 accounts.accountsDb.setTransactionSuccessful(); 2402 } finally { 2403 accounts.accountsDb.endTransaction(); 2404 } 2405 if (isChanged) { 2406 removeAccountFromCacheLocked(accounts, account); 2407 for (Entry<String, Integer> packageToVisibility : packagesToVisibility 2408 .entrySet()) { 2409 if ((packageToVisibility.getValue() == AccountManager.VISIBILITY_VISIBLE) 2410 || (packageToVisibility.getValue() 2411 == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)) { 2412 notifyPackage(packageToVisibility.getKey(), accounts); 2413 } 2414 } 2415 2416 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occurred. 2417 sendAccountsChangedBroadcast(accounts.userId); 2418 for (String packageName : accountRemovedReceivers) { 2419 sendAccountRemovedBroadcast(account, packageName, accounts.userId); 2420 } 2421 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE 2422 : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE; 2423 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts); 2424 } 2425 } 2426 } 2427 final long id = Binder.clearCallingIdentity(); 2428 try { 2429 int parentUserId = accounts.userId; 2430 if (canHaveProfile(parentUserId)) { 2431 // Remove from any restricted profiles that are sharing this account. 2432 List<UserInfo> users = getUserManager().getAliveUsers(); 2433 for (UserInfo user : users) { 2434 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) { 2435 removeSharedAccountAsUser(account, user.id, callingUid); 2436 } 2437 } 2438 } 2439 } finally { 2440 Binder.restoreCallingIdentity(id); 2441 } 2442 2443 if (isChanged) { 2444 synchronized (accounts.credentialsPermissionNotificationIds) { 2445 for (Pair<Pair<Account, String>, Integer> key 2446 : accounts.credentialsPermissionNotificationIds.keySet()) { 2447 if (account.equals(key.first.first) 2448 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) { 2449 final int uid = (Integer) key.second; 2450 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( 2451 account, uid, false)); 2452 } 2453 } 2454 } 2455 } 2456 2457 AccountManager.invalidateLocalAccountUserDataCaches(); 2458 2459 return isChanged; 2460 } 2461 2462 @Override invalidateAuthToken(String accountType, String authToken)2463 public void invalidateAuthToken(String accountType, String authToken) { 2464 int callerUid = Binder.getCallingUid(); 2465 Objects.requireNonNull(accountType, "accountType cannot be null"); 2466 Objects.requireNonNull(authToken, "authToken cannot be null"); 2467 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2468 Log.v(TAG, "invalidateAuthToken: accountType " + accountType 2469 + ", caller's uid " + callerUid 2470 + ", pid " + Binder.getCallingPid()); 2471 } 2472 int userId = UserHandle.getCallingUserId(); 2473 final long identityToken = clearCallingIdentity(); 2474 try { 2475 UserAccounts accounts = getUserAccounts(userId); 2476 List<Pair<Account, String>> deletedTokens; 2477 synchronized (accounts.dbLock) { 2478 accounts.accountsDb.beginTransaction(); 2479 try { 2480 deletedTokens = invalidateAuthTokenLocked(accounts, accountType, authToken); 2481 accounts.accountsDb.setTransactionSuccessful(); 2482 } finally { 2483 accounts.accountsDb.endTransaction(); 2484 } 2485 synchronized (accounts.cacheLock) { 2486 for (Pair<Account, String> tokenInfo : deletedTokens) { 2487 Account act = tokenInfo.first; 2488 String tokenType = tokenInfo.second; 2489 writeAuthTokenIntoCacheLocked(accounts, act, tokenType, null); 2490 } 2491 // wipe out cached token in memory. 2492 accounts.accountTokenCaches.remove(accountType, authToken); 2493 } 2494 } 2495 } finally { 2496 restoreCallingIdentity(identityToken); 2497 } 2498 } 2499 invalidateAuthTokenLocked(UserAccounts accounts, String accountType, String authToken)2500 private List<Pair<Account, String>> invalidateAuthTokenLocked(UserAccounts accounts, String accountType, 2501 String authToken) { 2502 // TODO Move to AccountsDB 2503 List<Pair<Account, String>> results = new ArrayList<>(); 2504 Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken); 2505 2506 try { 2507 while (cursor.moveToNext()) { 2508 String authTokenId = cursor.getString(0); 2509 String accountName = cursor.getString(1); 2510 String authTokenType = cursor.getString(2); 2511 accounts.accountsDb.deleteAuthToken(authTokenId); 2512 results.add(Pair.create(new Account(accountName, accountType), authTokenType)); 2513 } 2514 } finally { 2515 cursor.close(); 2516 } 2517 return results; 2518 } 2519 saveCachedToken( UserAccounts accounts, Account account, String callerPkg, byte[] callerSigDigest, String tokenType, String token, long expiryMillis)2520 private void saveCachedToken( 2521 UserAccounts accounts, 2522 Account account, 2523 String callerPkg, 2524 byte[] callerSigDigest, 2525 String tokenType, 2526 String token, 2527 long expiryMillis) { 2528 2529 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) { 2530 return; 2531 } 2532 cancelNotification(getSigninRequiredNotificationId(accounts, account), 2533 UserHandle.of(accounts.userId)); 2534 synchronized (accounts.cacheLock) { 2535 accounts.accountTokenCaches.put( 2536 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis); 2537 } 2538 } 2539 saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, String authToken)2540 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, 2541 String authToken) { 2542 if (account == null || type == null) { 2543 return false; 2544 } 2545 cancelNotification(getSigninRequiredNotificationId(accounts, account), 2546 UserHandle.of(accounts.userId)); 2547 synchronized (accounts.dbLock) { 2548 accounts.accountsDb.beginTransaction(); 2549 boolean updateCache = false; 2550 try { 2551 long accountId = accounts.accountsDb.findDeAccountId(account); 2552 if (accountId < 0) { 2553 return false; 2554 } 2555 accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type); 2556 if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) { 2557 accounts.accountsDb.setTransactionSuccessful(); 2558 updateCache = true; 2559 return true; 2560 } 2561 return false; 2562 } finally { 2563 accounts.accountsDb.endTransaction(); 2564 if (updateCache) { 2565 synchronized (accounts.cacheLock) { 2566 writeAuthTokenIntoCacheLocked(accounts, account, type, authToken); 2567 } 2568 } 2569 } 2570 } 2571 } 2572 2573 @Override peekAuthToken(Account account, String authTokenType)2574 public String peekAuthToken(Account account, String authTokenType) { 2575 final int callingUid = Binder.getCallingUid(); 2576 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2577 Log.v(TAG, "peekAuthToken: " + account 2578 + ", authTokenType " + authTokenType 2579 + ", caller's uid " + callingUid 2580 + ", pid " + Binder.getCallingPid()); 2581 } 2582 Objects.requireNonNull(account, "account cannot be null"); 2583 Objects.requireNonNull(authTokenType, "authTokenType cannot be null"); 2584 int userId = UserHandle.getCallingUserId(); 2585 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2586 String msg = String.format( 2587 "uid %s cannot peek the authtokens associated with accounts of type: %s", 2588 callingUid, 2589 account.type); 2590 throw new SecurityException(msg); 2591 } 2592 if (!isLocalUnlockedUser(userId)) { 2593 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid " 2594 + callingUid); 2595 return null; 2596 } 2597 final long identityToken = clearCallingIdentity(); 2598 try { 2599 UserAccounts accounts = getUserAccounts(userId); 2600 return readAuthTokenInternal(accounts, account, authTokenType); 2601 } finally { 2602 restoreCallingIdentity(identityToken); 2603 } 2604 } 2605 2606 @Override setAuthToken(Account account, String authTokenType, String authToken)2607 public void setAuthToken(Account account, String authTokenType, String authToken) { 2608 final int callingUid = Binder.getCallingUid(); 2609 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2610 Log.v(TAG, "setAuthToken: " + account 2611 + ", authTokenType " + authTokenType 2612 + ", caller's uid " + callingUid 2613 + ", pid " + Binder.getCallingPid()); 2614 } 2615 Objects.requireNonNull(account, "account cannot be null"); 2616 Objects.requireNonNull(authTokenType, "authTokenType cannot be null"); 2617 int userId = UserHandle.getCallingUserId(); 2618 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2619 String msg = String.format( 2620 "uid %s cannot set auth tokens associated with accounts of type: %s", 2621 callingUid, 2622 account.type); 2623 throw new SecurityException(msg); 2624 } 2625 final long identityToken = clearCallingIdentity(); 2626 try { 2627 UserAccounts accounts = getUserAccounts(userId); 2628 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); 2629 } finally { 2630 restoreCallingIdentity(identityToken); 2631 } 2632 } 2633 2634 @Override setPassword(Account account, String password)2635 public void setPassword(Account account, String password) { 2636 final int callingUid = Binder.getCallingUid(); 2637 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2638 Log.v(TAG, "setAuthToken: " + account 2639 + ", caller's uid " + callingUid 2640 + ", pid " + Binder.getCallingPid()); 2641 } 2642 Objects.requireNonNull(account, "account cannot be null"); 2643 int userId = UserHandle.getCallingUserId(); 2644 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2645 String msg = String.format( 2646 "uid %s cannot set secrets for accounts of type: %s", 2647 callingUid, 2648 account.type); 2649 throw new SecurityException(msg); 2650 } 2651 final long identityToken = clearCallingIdentity(); 2652 try { 2653 UserAccounts accounts = getUserAccounts(userId); 2654 setPasswordInternal(accounts, account, password, callingUid); 2655 } finally { 2656 restoreCallingIdentity(identityToken); 2657 } 2658 } 2659 setPasswordInternal(UserAccounts accounts, Account account, String password, int callingUid)2660 private void setPasswordInternal(UserAccounts accounts, Account account, String password, 2661 int callingUid) { 2662 if (account == null) { 2663 return; 2664 } 2665 boolean isChanged = false; 2666 synchronized (accounts.dbLock) { 2667 synchronized (accounts.cacheLock) { 2668 accounts.accountsDb.beginTransaction(); 2669 try { 2670 final long accountId = accounts.accountsDb.findDeAccountId(account); 2671 if (accountId >= 0) { 2672 accounts.accountsDb.updateCeAccountPassword(accountId, password); 2673 accounts.accountsDb.deleteAuthTokensByAccountId(accountId); 2674 accounts.authTokenCache.remove(account); 2675 accounts.accountTokenCaches.remove(account); 2676 accounts.accountsDb.setTransactionSuccessful(); 2677 // If there is an account whose password will be updated and the database 2678 // transactions succeed, then we say that a change has occured. Even if the 2679 // new password is the same as the old and there were no authtokens to 2680 // delete. 2681 isChanged = true; 2682 String action = (password == null || password.length() == 0) ? 2683 AccountsDb.DEBUG_ACTION_CLEAR_PASSWORD 2684 : AccountsDb.DEBUG_ACTION_SET_PASSWORD; 2685 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, 2686 callingUid); 2687 } 2688 } finally { 2689 accounts.accountsDb.endTransaction(); 2690 if (isChanged) { 2691 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed. 2692 sendNotificationAccountUpdated(account, accounts); 2693 sendAccountsChangedBroadcast(accounts.userId); 2694 } 2695 } 2696 } 2697 } 2698 } 2699 2700 @Override clearPassword(Account account)2701 public void clearPassword(Account account) { 2702 final int callingUid = Binder.getCallingUid(); 2703 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2704 Log.v(TAG, "clearPassword: " + account 2705 + ", caller's uid " + callingUid 2706 + ", pid " + Binder.getCallingPid()); 2707 } 2708 Objects.requireNonNull(account, "account cannot be null"); 2709 int userId = UserHandle.getCallingUserId(); 2710 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2711 String msg = String.format( 2712 "uid %s cannot clear passwords for accounts of type: %s", 2713 callingUid, 2714 account.type); 2715 throw new SecurityException(msg); 2716 } 2717 final long identityToken = clearCallingIdentity(); 2718 try { 2719 UserAccounts accounts = getUserAccounts(userId); 2720 setPasswordInternal(accounts, account, null, callingUid); 2721 } finally { 2722 restoreCallingIdentity(identityToken); 2723 } 2724 } 2725 2726 @Override setUserData(Account account, String key, String value)2727 public void setUserData(Account account, String key, String value) { 2728 final int callingUid = Binder.getCallingUid(); 2729 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2730 Log.v(TAG, "setUserData: " + account 2731 + ", key " + key 2732 + ", caller's uid " + callingUid 2733 + ", pid " + Binder.getCallingPid()); 2734 } 2735 if (key == null) throw new IllegalArgumentException("key is null"); 2736 if (account == null) throw new IllegalArgumentException("account is null"); 2737 int userId = UserHandle.getCallingUserId(); 2738 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 2739 String msg = String.format( 2740 "uid %s cannot set user data for accounts of type: %s", 2741 callingUid, 2742 account.type); 2743 throw new SecurityException(msg); 2744 } 2745 final long identityToken = clearCallingIdentity(); 2746 try { 2747 UserAccounts accounts = getUserAccounts(userId); 2748 if (!accountExistsCache(accounts, account)) { 2749 return; 2750 } 2751 setUserdataInternal(accounts, account, key, value); 2752 } finally { 2753 restoreCallingIdentity(identityToken); 2754 } 2755 } 2756 accountExistsCache(UserAccounts accounts, Account account)2757 private boolean accountExistsCache(UserAccounts accounts, Account account) { 2758 synchronized (accounts.cacheLock) { 2759 if (accounts.accountCache.containsKey(account.type)) { 2760 for (Account acc : accounts.accountCache.get(account.type)) { 2761 if (acc.name.equals(account.name)) { 2762 return true; 2763 } 2764 } 2765 } 2766 } 2767 return false; 2768 } 2769 setUserdataInternal(UserAccounts accounts, Account account, String key, String value)2770 private void setUserdataInternal(UserAccounts accounts, Account account, String key, 2771 String value) { 2772 synchronized (accounts.dbLock) { 2773 accounts.accountsDb.beginTransaction(); 2774 try { 2775 long accountId = accounts.accountsDb.findDeAccountId(account); 2776 if (accountId < 0) { 2777 return; 2778 } 2779 long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key); 2780 if (extrasId < 0) { 2781 extrasId = accounts.accountsDb.insertExtra(accountId, key, value); 2782 if (extrasId < 0) { 2783 return; 2784 } 2785 } else if (!accounts.accountsDb.updateExtra(extrasId, value)) { 2786 return; 2787 } 2788 accounts.accountsDb.setTransactionSuccessful(); 2789 } finally { 2790 accounts.accountsDb.endTransaction(); 2791 } 2792 synchronized (accounts.cacheLock) { 2793 writeUserDataIntoCacheLocked(accounts, account, key, value); 2794 AccountManager.invalidateLocalAccountUserDataCaches(); 2795 } 2796 } 2797 } 2798 onResult(IAccountManagerResponse response, Bundle result)2799 private void onResult(IAccountManagerResponse response, Bundle result) { 2800 if (result == null) { 2801 Log.e(TAG, "the result is unexpectedly null", new Exception()); 2802 } 2803 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2804 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2805 + response); 2806 } 2807 try { 2808 response.onResult(result); 2809 } catch (RemoteException e) { 2810 // if the caller is dead then there is no one to care about remote 2811 // exceptions 2812 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2813 Log.v(TAG, "failure while notifying response", e); 2814 } 2815 } 2816 } 2817 2818 @Override getAuthTokenLabel(IAccountManagerResponse response, final String accountType, final String authTokenType)2819 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, 2820 final String authTokenType) 2821 throws RemoteException { 2822 Preconditions.checkArgument(accountType != null, "accountType cannot be null"); 2823 Preconditions.checkArgument(authTokenType != null, "authTokenType cannot be null"); 2824 2825 final int callingUid = getCallingUid(); 2826 clearCallingIdentity(); 2827 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { 2828 throw new SecurityException("can only call from system"); 2829 } 2830 int userId = UserHandle.getUserId(callingUid); 2831 final long identityToken = clearCallingIdentity(); 2832 try { 2833 UserAccounts accounts = getUserAccounts(userId); 2834 new Session(accounts, response, accountType, false /* expectActivityLaunch */, 2835 false /* stripAuthTokenFromResult */, null /* accountName */, 2836 false /* authDetailsRequired */) { 2837 @Override 2838 protected String toDebugString(long now) { 2839 return super.toDebugString(now) + ", getAuthTokenLabel" 2840 + ", " + accountType 2841 + ", authTokenType " + authTokenType; 2842 } 2843 2844 @Override 2845 public void run() throws RemoteException { 2846 mAuthenticator.getAuthTokenLabel(this, authTokenType); 2847 } 2848 2849 @Override 2850 public void onResult(Bundle result) { 2851 Bundle.setDefusable(result, true); 2852 if (result != null) { 2853 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); 2854 Bundle bundle = new Bundle(); 2855 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); 2856 super.onResult(bundle); 2857 return; 2858 } else { 2859 super.onResult(result); 2860 } 2861 } 2862 }.bind(); 2863 } finally { 2864 restoreCallingIdentity(identityToken); 2865 } 2866 } 2867 2868 @Override getAuthToken( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, final boolean expectActivityLaunch, final Bundle loginOptions)2869 public void getAuthToken( 2870 IAccountManagerResponse response, 2871 final Account account, 2872 final String authTokenType, 2873 final boolean notifyOnAuthFailure, 2874 final boolean expectActivityLaunch, 2875 final Bundle loginOptions) { 2876 Bundle.setDefusable(loginOptions, true); 2877 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2878 Log.v(TAG, "getAuthToken: " + account 2879 + ", response " + response 2880 + ", authTokenType " + authTokenType 2881 + ", notifyOnAuthFailure " + notifyOnAuthFailure 2882 + ", expectActivityLaunch " + expectActivityLaunch 2883 + ", caller's uid " + Binder.getCallingUid() 2884 + ", pid " + Binder.getCallingPid()); 2885 } 2886 Preconditions.checkArgument(response != null, "response cannot be null"); 2887 try { 2888 if (account == null) { 2889 Slog.w(TAG, "getAuthToken called with null account"); 2890 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null"); 2891 return; 2892 } 2893 if (authTokenType == null) { 2894 Slog.w(TAG, "getAuthToken called with null authTokenType"); 2895 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null"); 2896 return; 2897 } 2898 } catch (RemoteException e) { 2899 Slog.w(TAG, "Failed to report error back to the client." + e); 2900 return; 2901 } 2902 int userId = UserHandle.getCallingUserId(); 2903 final long ident = Binder.clearCallingIdentity(); 2904 final UserAccounts accounts; 2905 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 2906 try { 2907 accounts = getUserAccounts(userId); 2908 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 2909 AuthenticatorDescription.newKey(account.type), accounts.userId); 2910 } finally { 2911 Binder.restoreCallingIdentity(ident); 2912 } 2913 2914 final boolean customTokens = 2915 authenticatorInfo != null && authenticatorInfo.type.customTokens; 2916 2917 // skip the check if customTokens 2918 final int callerUid = Binder.getCallingUid(); 2919 final boolean permissionGranted = 2920 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); 2921 2922 // Get the calling package. We will use it for the purpose of caching. 2923 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 2924 String[] callerOwnedPackageNames; 2925 final long ident2 = Binder.clearCallingIdentity(); 2926 try { 2927 callerOwnedPackageNames = mPackageManager.getPackagesForUid(callerUid); 2928 } finally { 2929 Binder.restoreCallingIdentity(ident2); 2930 } 2931 if (callerPkg == null || callerOwnedPackageNames == null 2932 || !ArrayUtils.contains(callerOwnedPackageNames, callerPkg)) { 2933 String msg = String.format( 2934 "Uid %s is attempting to illegally masquerade as package %s!", 2935 callerUid, 2936 callerPkg); 2937 throw new SecurityException(msg); 2938 } 2939 2940 // let authenticator know the identity of the caller 2941 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); 2942 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); 2943 2944 if (notifyOnAuthFailure) { 2945 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); 2946 } 2947 2948 final long identityToken = clearCallingIdentity(); 2949 try { 2950 // Distill the caller's package signatures into a single digest. 2951 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg); 2952 2953 // if the caller has permission, do the peek. otherwise go the more expensive 2954 // route of starting a Session 2955 if (!customTokens && permissionGranted) { 2956 String authToken = readAuthTokenInternal(accounts, account, authTokenType); 2957 if (authToken != null) { 2958 logGetAuthTokenMetrics(callerPkg, account.type); 2959 Bundle result = new Bundle(); 2960 result.putString(AccountManager.KEY_AUTHTOKEN, authToken); 2961 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2962 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2963 onResult(response, result); 2964 return; 2965 } 2966 } 2967 2968 if (customTokens) { 2969 /* 2970 * Look up tokens in the new cache only if the loginOptions don't have parameters 2971 * outside of those expected to be injected by the AccountManager, e.g. 2972 * ANDORID_PACKAGE_NAME. 2973 */ 2974 String token = readCachedTokenInternal( 2975 accounts, 2976 account, 2977 authTokenType, 2978 callerPkg, 2979 callerPkgSigDigest); 2980 if (token != null) { 2981 logGetAuthTokenMetrics(callerPkg, account.type); 2982 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2983 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); 2984 } 2985 Bundle result = new Bundle(); 2986 result.putString(AccountManager.KEY_AUTHTOKEN, token); 2987 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 2988 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 2989 onResult(response, result); 2990 return; 2991 } 2992 } 2993 2994 new Session( 2995 accounts, 2996 response, 2997 account.type, 2998 expectActivityLaunch, 2999 false /* stripAuthTokenFromResult */, 3000 account.name, 3001 false /* authDetailsRequired */) { 3002 @Override 3003 protected String toDebugString(long now) { 3004 if (loginOptions != null) loginOptions.keySet(); 3005 return super.toDebugString(now) + ", getAuthToken" 3006 + ", " + account.toSafeString() 3007 + ", authTokenType " + authTokenType 3008 + ", loginOptions " + loginOptions 3009 + ", notifyOnAuthFailure " + notifyOnAuthFailure; 3010 } 3011 3012 @Override 3013 public void run() throws RemoteException { 3014 // If the caller doesn't have permission then create and return the 3015 // "grant permission" intent instead of the "getAuthToken" intent. 3016 if (!permissionGranted) { 3017 mAuthenticator.getAuthTokenLabel(this, authTokenType); 3018 } else { 3019 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); 3020 logGetAuthTokenMetrics(callerPkg, account.type); 3021 } 3022 } 3023 3024 @Override 3025 public void onResult(Bundle result) { 3026 Bundle.setDefusable(result, true); 3027 if (result != null) { 3028 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { 3029 Intent intent = newGrantCredentialsPermissionIntent( 3030 account, 3031 null, 3032 callerUid, 3033 new AccountAuthenticatorResponse(this), 3034 authTokenType, 3035 true); 3036 Bundle bundle = new Bundle(); 3037 bundle.putParcelable(AccountManager.KEY_INTENT, intent); 3038 onResult(bundle); 3039 return; 3040 } 3041 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 3042 if (authToken != null) { 3043 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); 3044 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 3045 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { 3046 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 3047 "the type and name should not be empty"); 3048 return; 3049 } 3050 Account resultAccount = new Account(name, type); 3051 if (!customTokens) { 3052 saveAuthTokenToDatabase( 3053 mAccounts, 3054 resultAccount, 3055 authTokenType, 3056 authToken); 3057 } 3058 long expiryMillis = result.getLong( 3059 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L); 3060 if (customTokens 3061 && expiryMillis > System.currentTimeMillis()) { 3062 saveCachedToken( 3063 mAccounts, 3064 account, 3065 callerPkg, 3066 callerPkgSigDigest, 3067 authTokenType, 3068 authToken, 3069 expiryMillis); 3070 } 3071 } 3072 3073 Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 3074 if (intent != null && notifyOnAuthFailure && !customTokens) { 3075 /* 3076 * Make sure that the supplied intent is owned by the authenticator 3077 * giving it to the system. Otherwise a malicious authenticator could 3078 * have users launching arbitrary activities by tricking users to 3079 * interact with malicious notifications. 3080 */ 3081 if (!checkKeyIntent( 3082 Binder.getCallingUid(), 3083 intent)) { 3084 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 3085 "invalid intent in bundle returned"); 3086 return; 3087 } 3088 doNotification( 3089 mAccounts, 3090 account, 3091 result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), 3092 intent, "android", accounts.userId); 3093 } 3094 } 3095 super.onResult(result); 3096 } 3097 }.bind(); 3098 } finally { 3099 restoreCallingIdentity(identityToken); 3100 } 3101 } 3102 logGetAuthTokenMetrics(final String callerPackage, String accountType)3103 private void logGetAuthTokenMetrics(final String callerPackage, String accountType) { 3104 // Although this is not a 'device policy' API, enterprise is the current use case. 3105 DevicePolicyEventLogger 3106 .createEvent(DevicePolicyEnums.GET_ACCOUNT_AUTH_TOKEN) 3107 .setStrings( 3108 TextUtils.emptyIfNull(callerPackage), 3109 TextUtils.emptyIfNull(accountType)) 3110 .write(); 3111 } 3112 calculatePackageSignatureDigest(String callerPkg)3113 private byte[] calculatePackageSignatureDigest(String callerPkg) { 3114 MessageDigest digester; 3115 try { 3116 digester = MessageDigest.getInstance("SHA-256"); 3117 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 3118 callerPkg, PackageManager.GET_SIGNATURES); 3119 for (Signature sig : pkgInfo.signatures) { 3120 digester.update(sig.toByteArray()); 3121 } 3122 } catch (NoSuchAlgorithmException x) { 3123 Log.wtf(TAG, "SHA-256 should be available", x); 3124 digester = null; 3125 } catch (NameNotFoundException e) { 3126 Log.w(TAG, "Could not find packageinfo for: " + callerPkg); 3127 digester = null; 3128 } 3129 return (digester == null) ? null : digester.digest(); 3130 } 3131 createNoCredentialsPermissionNotification(Account account, Intent intent, String packageName, int userId)3132 private void createNoCredentialsPermissionNotification(Account account, Intent intent, 3133 String packageName, int userId) { 3134 int uid = intent.getIntExtra( 3135 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); 3136 String authTokenType = intent.getStringExtra( 3137 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); 3138 final String titleAndSubtitle = 3139 mContext.getString(R.string.permission_request_notification_for_app_with_subtitle, 3140 getApplicationLabel(packageName), account.name); 3141 final int index = titleAndSubtitle.indexOf('\n'); 3142 String title = titleAndSubtitle; 3143 String subtitle = ""; 3144 if (index > 0) { 3145 title = titleAndSubtitle.substring(0, index); 3146 subtitle = titleAndSubtitle.substring(index + 1); 3147 } 3148 UserHandle user = UserHandle.of(userId); 3149 Context contextForUser = getContextForUser(user); 3150 Notification n = 3151 new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) 3152 .setSmallIcon(android.R.drawable.stat_sys_warning) 3153 .setWhen(0) 3154 .setColor(contextForUser.getColor( 3155 com.android.internal.R.color.system_notification_accent_color)) 3156 .setContentTitle(title) 3157 .setContentText(subtitle) 3158 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, 3159 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 3160 null, user)) 3161 .build(); 3162 installNotification(getCredentialPermissionNotificationId( 3163 account, authTokenType, uid), n, "android", user.getIdentifier()); 3164 } 3165 getApplicationLabel(String packageName)3166 private String getApplicationLabel(String packageName) { 3167 try { 3168 return mPackageManager.getApplicationLabel( 3169 mPackageManager.getApplicationInfo(packageName, 0)).toString(); 3170 } catch (PackageManager.NameNotFoundException e) { 3171 return packageName; 3172 } 3173 } 3174 newGrantCredentialsPermissionIntent(Account account, String packageName, int uid, AccountAuthenticatorResponse response, String authTokenType, boolean startInNewTask)3175 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, 3176 int uid, AccountAuthenticatorResponse response, String authTokenType, 3177 boolean startInNewTask) { 3178 3179 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); 3180 3181 if (startInNewTask) { 3182 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. 3183 // Since it was set in Eclair+ we can't change it without breaking apps using 3184 // the intent from a non-Activity context. This is the default behavior. 3185 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3186 } 3187 intent.addCategory(getCredentialPermissionNotificationId(account, 3188 authTokenType, uid).mTag + (packageName != null ? packageName : "")); 3189 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); 3190 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); 3191 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); 3192 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); 3193 3194 return intent; 3195 } 3196 getCredentialPermissionNotificationId(Account account, String authTokenType, int uid)3197 private NotificationId getCredentialPermissionNotificationId(Account account, 3198 String authTokenType, int uid) { 3199 NotificationId nId; 3200 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3201 synchronized (accounts.credentialsPermissionNotificationIds) { 3202 final Pair<Pair<Account, String>, Integer> key = 3203 new Pair<Pair<Account, String>, Integer>( 3204 new Pair<Account, String>(account, authTokenType), uid); 3205 nId = accounts.credentialsPermissionNotificationIds.get(key); 3206 if (nId == null) { 3207 String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION 3208 + ":" + account.hashCode() + ":" + authTokenType.hashCode() + ":" + uid; 3209 int id = SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION; 3210 nId = new NotificationId(tag, id); 3211 accounts.credentialsPermissionNotificationIds.put(key, nId); 3212 } 3213 } 3214 return nId; 3215 } 3216 getSigninRequiredNotificationId(UserAccounts accounts, Account account)3217 private NotificationId getSigninRequiredNotificationId(UserAccounts accounts, Account account) { 3218 NotificationId nId; 3219 synchronized (accounts.signinRequiredNotificationIds) { 3220 nId = accounts.signinRequiredNotificationIds.get(account); 3221 if (nId == null) { 3222 String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN 3223 + ":" + account.hashCode(); 3224 int id = SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN; 3225 nId = new NotificationId(tag, id); 3226 accounts.signinRequiredNotificationIds.put(account, nId); 3227 } 3228 } 3229 return nId; 3230 } 3231 3232 @Override addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)3233 public void addAccount(final IAccountManagerResponse response, final String accountType, 3234 final String authTokenType, final String[] requiredFeatures, 3235 final boolean expectActivityLaunch, final Bundle optionsIn) { 3236 Bundle.setDefusable(optionsIn, true); 3237 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3238 Log.v(TAG, "addAccount: accountType " + accountType 3239 + ", response " + response 3240 + ", authTokenType " + authTokenType 3241 + ", requiredFeatures " + Arrays.toString(requiredFeatures) 3242 + ", expectActivityLaunch " + expectActivityLaunch 3243 + ", caller's uid " + Binder.getCallingUid() 3244 + ", pid " + Binder.getCallingPid()); 3245 } 3246 if (response == null) throw new IllegalArgumentException("response is null"); 3247 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 3248 3249 // Is user disallowed from modifying accounts? 3250 final int uid = Binder.getCallingUid(); 3251 final int userId = UserHandle.getUserId(uid); 3252 if (!canUserModifyAccounts(userId, uid)) { 3253 try { 3254 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 3255 "User is not allowed to add an account!"); 3256 } catch (RemoteException re) { 3257 } 3258 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3259 return; 3260 } 3261 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 3262 try { 3263 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3264 "User cannot modify accounts of this type (policy)."); 3265 } catch (RemoteException re) { 3266 } 3267 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3268 userId); 3269 return; 3270 } 3271 addAccountAndLogMetrics(response, accountType, authTokenType, requiredFeatures, 3272 expectActivityLaunch, optionsIn, userId); 3273 } 3274 3275 @Override addAccountAsUser(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn, int userId)3276 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 3277 final String authTokenType, final String[] requiredFeatures, 3278 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 3279 Bundle.setDefusable(optionsIn, true); 3280 int callingUid = Binder.getCallingUid(); 3281 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3282 Log.v(TAG, "addAccount: accountType " + accountType 3283 + ", response " + response 3284 + ", authTokenType " + authTokenType 3285 + ", requiredFeatures " + Arrays.toString(requiredFeatures) 3286 + ", expectActivityLaunch " + expectActivityLaunch 3287 + ", caller's uid " + Binder.getCallingUid() 3288 + ", pid " + Binder.getCallingPid() 3289 + ", for user id " + userId); 3290 } 3291 Preconditions.checkArgument(response != null, "response cannot be null"); 3292 Preconditions.checkArgument(accountType != null, "accountType cannot be null"); 3293 // Only allow the system process to add accounts of other users 3294 if (isCrossUser(callingUid, userId)) { 3295 throw new SecurityException( 3296 String.format( 3297 "User %s trying to add account for %s" , 3298 UserHandle.getCallingUserId(), 3299 userId)); 3300 } 3301 3302 // Is user disallowed from modifying accounts? 3303 if (!canUserModifyAccounts(userId, callingUid)) { 3304 try { 3305 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 3306 "User is not allowed to add an account!"); 3307 } catch (RemoteException re) { 3308 } 3309 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3310 return; 3311 } 3312 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 3313 try { 3314 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3315 "User cannot modify accounts of this type (policy)."); 3316 } catch (RemoteException re) { 3317 } 3318 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3319 userId); 3320 return; 3321 } 3322 addAccountAndLogMetrics(response, accountType, authTokenType, requiredFeatures, 3323 expectActivityLaunch, optionsIn, userId); 3324 } 3325 addAccountAndLogMetrics( IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn, int userId)3326 private void addAccountAndLogMetrics( 3327 IAccountManagerResponse response, String accountType, 3328 String authTokenType, String[] requiredFeatures, 3329 boolean expectActivityLaunch, Bundle optionsIn, int userId) { 3330 final int pid = Binder.getCallingPid(); 3331 final int uid = Binder.getCallingUid(); 3332 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 3333 options.putInt(AccountManager.KEY_CALLER_UID, uid); 3334 options.putInt(AccountManager.KEY_CALLER_PID, pid); 3335 3336 final long identityToken = clearCallingIdentity(); 3337 try { 3338 UserAccounts accounts = getUserAccounts(userId); 3339 logRecordWithUid( 3340 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, 3341 uid); 3342 new Session(accounts, response, accountType, expectActivityLaunch, 3343 true /* stripAuthTokenFromResult */, null /* accountName */, 3344 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 3345 @Override 3346 public void run() throws RemoteException { 3347 mAuthenticator.addAccount( 3348 this, mAccountType, authTokenType, requiredFeatures, options); 3349 String callerPackage = options.getString( 3350 AccountManager.KEY_ANDROID_PACKAGE_NAME); 3351 logAddAccountMetrics( 3352 callerPackage, accountType, requiredFeatures, authTokenType); 3353 } 3354 3355 @Override 3356 protected String toDebugString(long now) { 3357 return super.toDebugString(now) + ", addAccount" 3358 + ", accountType " + accountType 3359 + ", requiredFeatures " 3360 + (requiredFeatures != null 3361 ? TextUtils.join(",", requiredFeatures) 3362 : null); 3363 } 3364 }.bind(); 3365 } finally { 3366 restoreCallingIdentity(identityToken); 3367 } 3368 } 3369 logAddAccountMetrics( String callerPackage, String accountType, String[] requiredFeatures, String authTokenType)3370 private void logAddAccountMetrics( 3371 String callerPackage, String accountType, String[] requiredFeatures, 3372 String authTokenType) { 3373 // Although this is not a 'device policy' API, enterprise is the current use case. 3374 DevicePolicyEventLogger 3375 .createEvent(DevicePolicyEnums.ADD_ACCOUNT) 3376 .setStrings( 3377 TextUtils.emptyIfNull(accountType), 3378 TextUtils.emptyIfNull(callerPackage), 3379 TextUtils.emptyIfNull(authTokenType), 3380 requiredFeatures == null 3381 ? "" 3382 : TextUtils.join(";", requiredFeatures)) 3383 .write(); 3384 } 3385 3386 @Override startAddAccountSession( final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)3387 public void startAddAccountSession( 3388 final IAccountManagerResponse response, 3389 final String accountType, 3390 final String authTokenType, 3391 final String[] requiredFeatures, 3392 final boolean expectActivityLaunch, 3393 final Bundle optionsIn) { 3394 Bundle.setDefusable(optionsIn, true); 3395 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3396 Log.v(TAG, 3397 "startAddAccountSession: accountType " + accountType 3398 + ", response " + response 3399 + ", authTokenType " + authTokenType 3400 + ", requiredFeatures " + Arrays.toString(requiredFeatures) 3401 + ", expectActivityLaunch " + expectActivityLaunch 3402 + ", caller's uid " + Binder.getCallingUid() 3403 + ", pid " + Binder.getCallingPid()); 3404 } 3405 Preconditions.checkArgument(response != null, "response cannot be null"); 3406 Preconditions.checkArgument(accountType != null, "accountType cannot be null"); 3407 3408 final int uid = Binder.getCallingUid(); 3409 final int userId = UserHandle.getUserId(uid); 3410 if (!canUserModifyAccounts(userId, uid)) { 3411 try { 3412 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 3413 "User is not allowed to add an account!"); 3414 } catch (RemoteException re) { 3415 } 3416 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3417 return; 3418 } 3419 if (!canUserModifyAccountsForType(userId, accountType, uid)) { 3420 try { 3421 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3422 "User cannot modify accounts of this type (policy)."); 3423 } catch (RemoteException re) { 3424 } 3425 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3426 userId); 3427 return; 3428 } 3429 final int pid = Binder.getCallingPid(); 3430 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 3431 options.putInt(AccountManager.KEY_CALLER_UID, uid); 3432 options.putInt(AccountManager.KEY_CALLER_PID, pid); 3433 3434 // Check to see if the Password should be included to the caller. 3435 String callerPkg = options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 3436 boolean isPasswordForwardingAllowed = checkPermissionAndNote( 3437 callerPkg, uid, Manifest.permission.GET_PASSWORD); 3438 3439 final long identityToken = clearCallingIdentity(); 3440 try { 3441 UserAccounts accounts = getUserAccounts(userId); 3442 logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD, 3443 AccountsDb.TABLE_ACCOUNTS, uid); 3444 new StartAccountSession( 3445 accounts, 3446 response, 3447 accountType, 3448 expectActivityLaunch, 3449 null /* accountName */, 3450 false /* authDetailsRequired */, 3451 true /* updateLastAuthenticationTime */, 3452 isPasswordForwardingAllowed) { 3453 @Override 3454 public void run() throws RemoteException { 3455 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType, 3456 requiredFeatures, options); 3457 logAddAccountMetrics(callerPkg, accountType, requiredFeatures, authTokenType); 3458 } 3459 3460 @Override 3461 protected String toDebugString(long now) { 3462 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures); 3463 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType " 3464 + accountType + ", requiredFeatures " 3465 + (requiredFeatures != null ? requiredFeaturesStr : null); 3466 } 3467 }.bind(); 3468 } finally { 3469 restoreCallingIdentity(identityToken); 3470 } 3471 } 3472 3473 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */ 3474 private abstract class StartAccountSession extends Session { 3475 3476 private final boolean mIsPasswordForwardingAllowed; 3477 StartAccountSession( UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, String accountName, boolean authDetailsRequired, boolean updateLastAuthenticationTime, boolean isPasswordForwardingAllowed)3478 public StartAccountSession( 3479 UserAccounts accounts, 3480 IAccountManagerResponse response, 3481 String accountType, 3482 boolean expectActivityLaunch, 3483 String accountName, 3484 boolean authDetailsRequired, 3485 boolean updateLastAuthenticationTime, 3486 boolean isPasswordForwardingAllowed) { 3487 super(accounts, response, accountType, expectActivityLaunch, 3488 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired, 3489 updateLastAuthenticationTime); 3490 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed; 3491 } 3492 3493 @Override onResult(Bundle result)3494 public void onResult(Bundle result) { 3495 Bundle.setDefusable(result, true); 3496 mNumResults++; 3497 Intent intent = null; 3498 if (result != null 3499 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 3500 if (!checkKeyIntent( 3501 Binder.getCallingUid(), 3502 intent)) { 3503 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 3504 "invalid intent in bundle returned"); 3505 return; 3506 } 3507 } 3508 IAccountManagerResponse response; 3509 if (mExpectActivityLaunch && result != null 3510 && result.containsKey(AccountManager.KEY_INTENT)) { 3511 response = mResponse; 3512 } else { 3513 response = getResponseAndClose(); 3514 } 3515 if (response == null) { 3516 return; 3517 } 3518 if (result == null) { 3519 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3520 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response " 3521 + response); 3522 } 3523 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 3524 "null bundle returned"); 3525 return; 3526 } 3527 3528 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) { 3529 // All AccountManager error codes are greater 3530 // than 0 3531 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE), 3532 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3533 return; 3534 } 3535 3536 // Omit passwords if the caller isn't permitted to see them. 3537 if (!mIsPasswordForwardingAllowed) { 3538 result.remove(AccountManager.KEY_PASSWORD); 3539 } 3540 3541 // Strip auth token from result. 3542 result.remove(AccountManager.KEY_AUTHTOKEN); 3543 3544 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3545 Log.v(TAG, 3546 getClass().getSimpleName() + " calling onResult() on response " + response); 3547 } 3548 3549 // Get the session bundle created by authenticator. The 3550 // bundle contains data necessary for finishing the session 3551 // later. The session bundle will be encrypted here and 3552 // decrypted later when trying to finish the session. 3553 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3554 if (sessionBundle != null) { 3555 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 3556 if (TextUtils.isEmpty(accountType) 3557 || !mAccountType.equalsIgnoreCase(accountType)) { 3558 Log.w(TAG, "Account type in session bundle doesn't match request."); 3559 } 3560 // Add accountType info to session bundle. This will 3561 // override any value set by authenticator. 3562 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType); 3563 3564 // Encrypt session bundle before returning to caller. 3565 try { 3566 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 3567 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle); 3568 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle); 3569 } catch (GeneralSecurityException e) { 3570 if (Log.isLoggable(TAG, Log.DEBUG)) { 3571 Log.v(TAG, "Failed to encrypt session bundle!", e); 3572 } 3573 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE, 3574 "failed to encrypt session bundle"); 3575 return; 3576 } 3577 } 3578 3579 sendResponse(response, result); 3580 } 3581 } 3582 3583 @Override finishSessionAsUser(IAccountManagerResponse response, @NonNull Bundle sessionBundle, boolean expectActivityLaunch, Bundle appInfo, int userId)3584 public void finishSessionAsUser(IAccountManagerResponse response, 3585 @NonNull Bundle sessionBundle, 3586 boolean expectActivityLaunch, 3587 Bundle appInfo, 3588 int userId) { 3589 Bundle.setDefusable(sessionBundle, true); 3590 int callingUid = Binder.getCallingUid(); 3591 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3592 Log.v(TAG, 3593 "finishSession: response "+ response 3594 + ", expectActivityLaunch " + expectActivityLaunch 3595 + ", caller's uid " + callingUid 3596 + ", caller's user id " + UserHandle.getCallingUserId() 3597 + ", pid " + Binder.getCallingPid() 3598 + ", for user id " + userId); 3599 } 3600 Preconditions.checkArgument(response != null, "response cannot be null"); 3601 // Session bundle is the encrypted bundle of the original bundle created by authenticator. 3602 // Account type is added to it before encryption. 3603 if (sessionBundle == null || sessionBundle.size() == 0) { 3604 throw new IllegalArgumentException("sessionBundle is empty"); 3605 } 3606 3607 // Only allow the system process to finish session for other users. 3608 if (isCrossUser(callingUid, userId)) { 3609 throw new SecurityException( 3610 String.format( 3611 "User %s trying to finish session for %s without cross user permission", 3612 UserHandle.getCallingUserId(), 3613 userId)); 3614 } 3615 3616 if (!canUserModifyAccounts(userId, callingUid)) { 3617 sendErrorResponse(response, 3618 AccountManager.ERROR_CODE_USER_RESTRICTED, 3619 "User is not allowed to add an account!"); 3620 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 3621 return; 3622 } 3623 3624 final int pid = Binder.getCallingPid(); 3625 final Bundle decryptedBundle; 3626 final String accountType; 3627 // First decrypt session bundle to get account type for checking permission. 3628 try { 3629 CryptoHelper cryptoHelper = CryptoHelper.getInstance(); 3630 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle); 3631 if (decryptedBundle == null) { 3632 sendErrorResponse( 3633 response, 3634 AccountManager.ERROR_CODE_BAD_REQUEST, 3635 "failed to decrypt session bundle"); 3636 return; 3637 } 3638 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); 3639 // Account type cannot be null. This should not happen if session bundle was created 3640 // properly by #StartAccountSession. 3641 if (TextUtils.isEmpty(accountType)) { 3642 sendErrorResponse( 3643 response, 3644 AccountManager.ERROR_CODE_BAD_ARGUMENTS, 3645 "accountType is empty"); 3646 return; 3647 } 3648 3649 // If by any chances, decryptedBundle contains colliding keys with 3650 // system info 3651 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or 3652 // update credentials flow, we should replace with the new values of the current call. 3653 if (appInfo != null) { 3654 decryptedBundle.putAll(appInfo); 3655 } 3656 3657 // Add info that may be used by add account or update credentials flow. 3658 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid); 3659 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid); 3660 } catch (GeneralSecurityException e) { 3661 if (Log.isLoggable(TAG, Log.DEBUG)) { 3662 Log.v(TAG, "Failed to decrypt session bundle!", e); 3663 } 3664 sendErrorResponse( 3665 response, 3666 AccountManager.ERROR_CODE_BAD_REQUEST, 3667 "failed to decrypt session bundle"); 3668 return; 3669 } 3670 3671 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) { 3672 sendErrorResponse( 3673 response, 3674 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3675 "User cannot modify accounts of this type (policy)."); 3676 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 3677 userId); 3678 return; 3679 } 3680 3681 final long identityToken = clearCallingIdentity(); 3682 try { 3683 UserAccounts accounts = getUserAccounts(userId); 3684 logRecordWithUid( 3685 accounts, 3686 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH, 3687 AccountsDb.TABLE_ACCOUNTS, 3688 callingUid); 3689 new Session( 3690 accounts, 3691 response, 3692 accountType, 3693 expectActivityLaunch, 3694 true /* stripAuthTokenFromResult */, 3695 null /* accountName */, 3696 false /* authDetailsRequired */, 3697 true /* updateLastAuthenticationTime */) { 3698 @Override 3699 public void run() throws RemoteException { 3700 mAuthenticator.finishSession(this, mAccountType, decryptedBundle); 3701 } 3702 3703 @Override 3704 protected String toDebugString(long now) { 3705 return super.toDebugString(now) 3706 + ", finishSession" 3707 + ", accountType " + accountType; 3708 } 3709 }.bind(); 3710 } finally { 3711 restoreCallingIdentity(identityToken); 3712 } 3713 } 3714 showCantAddAccount(int errorCode, int userId)3715 private void showCantAddAccount(int errorCode, int userId) { 3716 final DevicePolicyManagerInternal dpmi = 3717 LocalServices.getService(DevicePolicyManagerInternal.class); 3718 Intent intent = null; 3719 if (dpmi == null) { 3720 intent = getDefaultCantAddAccountIntent(errorCode); 3721 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) { 3722 intent = dpmi.createUserRestrictionSupportIntent(userId, 3723 UserManager.DISALLOW_MODIFY_ACCOUNTS); 3724 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) { 3725 intent = dpmi.createShowAdminSupportIntent(userId, false); 3726 } 3727 if (intent == null) { 3728 intent = getDefaultCantAddAccountIntent(errorCode); 3729 } 3730 final long identityToken = clearCallingIdentity(); 3731 try { 3732 mContext.startActivityAsUser(intent, new UserHandle(userId)); 3733 } finally { 3734 restoreCallingIdentity(identityToken); 3735 } 3736 } 3737 3738 /** 3739 * Called when we don't know precisely who is preventing us from adding an account. 3740 */ getDefaultCantAddAccountIntent(int errorCode)3741 private Intent getDefaultCantAddAccountIntent(int errorCode) { 3742 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 3743 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 3744 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3745 return cantAddAccount; 3746 } 3747 3748 @Override confirmCredentialsAsUser( IAccountManagerResponse response, final Account account, final Bundle options, final boolean expectActivityLaunch, int userId)3749 public void confirmCredentialsAsUser( 3750 IAccountManagerResponse response, 3751 final Account account, 3752 final Bundle options, 3753 final boolean expectActivityLaunch, 3754 int userId) { 3755 Bundle.setDefusable(options, true); 3756 int callingUid = Binder.getCallingUid(); 3757 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3758 Log.v(TAG, "confirmCredentials: " + account 3759 + ", response " + response 3760 + ", expectActivityLaunch " + expectActivityLaunch 3761 + ", caller's uid " + callingUid 3762 + ", pid " + Binder.getCallingPid()); 3763 } 3764 // Only allow the system process to read accounts of other users 3765 if (isCrossUser(callingUid, userId)) { 3766 throw new SecurityException( 3767 String.format( 3768 "User %s trying to confirm account credentials for %s" , 3769 UserHandle.getCallingUserId(), 3770 userId)); 3771 } 3772 if (response == null) throw new IllegalArgumentException("response is null"); 3773 if (account == null) throw new IllegalArgumentException("account is null"); 3774 final long identityToken = clearCallingIdentity(); 3775 try { 3776 UserAccounts accounts = getUserAccounts(userId); 3777 new Session(accounts, response, account.type, expectActivityLaunch, 3778 true /* stripAuthTokenFromResult */, account.name, 3779 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) { 3780 @Override 3781 public void run() throws RemoteException { 3782 mAuthenticator.confirmCredentials(this, account, options); 3783 } 3784 @Override 3785 protected String toDebugString(long now) { 3786 return super.toDebugString(now) + ", confirmCredentials" 3787 + ", " + account.toSafeString(); 3788 } 3789 }.bind(); 3790 } finally { 3791 restoreCallingIdentity(identityToken); 3792 } 3793 } 3794 3795 @Override updateCredentials(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3796 public void updateCredentials(IAccountManagerResponse response, final Account account, 3797 final String authTokenType, final boolean expectActivityLaunch, 3798 final Bundle loginOptions) { 3799 Bundle.setDefusable(loginOptions, true); 3800 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3801 Log.v(TAG, "updateCredentials: " + account 3802 + ", response " + response 3803 + ", authTokenType " + authTokenType 3804 + ", expectActivityLaunch " + expectActivityLaunch 3805 + ", caller's uid " + Binder.getCallingUid() 3806 + ", pid " + Binder.getCallingPid()); 3807 } 3808 if (response == null) throw new IllegalArgumentException("response is null"); 3809 if (account == null) throw new IllegalArgumentException("account is null"); 3810 int userId = UserHandle.getCallingUserId(); 3811 final long identityToken = clearCallingIdentity(); 3812 try { 3813 UserAccounts accounts = getUserAccounts(userId); 3814 new Session(accounts, response, account.type, expectActivityLaunch, 3815 true /* stripAuthTokenFromResult */, account.name, 3816 false /* authDetailsRequired */, true /* updateLastCredentialTime */) { 3817 @Override 3818 public void run() throws RemoteException { 3819 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 3820 } 3821 @Override 3822 protected String toDebugString(long now) { 3823 if (loginOptions != null) loginOptions.keySet(); 3824 return super.toDebugString(now) + ", updateCredentials" 3825 + ", " + account.toSafeString() 3826 + ", authTokenType " + authTokenType 3827 + ", loginOptions " + loginOptions; 3828 } 3829 }.bind(); 3830 } finally { 3831 restoreCallingIdentity(identityToken); 3832 } 3833 } 3834 3835 @Override startUpdateCredentialsSession( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3836 public void startUpdateCredentialsSession( 3837 IAccountManagerResponse response, 3838 final Account account, 3839 final String authTokenType, 3840 final boolean expectActivityLaunch, 3841 final Bundle loginOptions) { 3842 Bundle.setDefusable(loginOptions, true); 3843 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3844 Log.v(TAG, 3845 "startUpdateCredentialsSession: " + account + ", response " + response 3846 + ", authTokenType " + authTokenType + ", expectActivityLaunch " 3847 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid() 3848 + ", pid " + Binder.getCallingPid()); 3849 } 3850 if (response == null) { 3851 throw new IllegalArgumentException("response is null"); 3852 } 3853 if (account == null) { 3854 throw new IllegalArgumentException("account is null"); 3855 } 3856 3857 final int uid = Binder.getCallingUid(); 3858 int userId = UserHandle.getCallingUserId(); 3859 3860 // Check to see if the Password should be included to the caller. 3861 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 3862 boolean isPasswordForwardingAllowed = checkPermissionAndNote( 3863 callerPkg, uid, Manifest.permission.GET_PASSWORD); 3864 3865 final long identityToken = clearCallingIdentity(); 3866 try { 3867 UserAccounts accounts = getUserAccounts(userId); 3868 new StartAccountSession( 3869 accounts, 3870 response, 3871 account.type, 3872 expectActivityLaunch, 3873 account.name, 3874 false /* authDetailsRequired */, 3875 true /* updateLastCredentialTime */, 3876 isPasswordForwardingAllowed) { 3877 @Override 3878 public void run() throws RemoteException { 3879 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, 3880 loginOptions); 3881 } 3882 3883 @Override 3884 protected String toDebugString(long now) { 3885 if (loginOptions != null) 3886 loginOptions.keySet(); 3887 return super.toDebugString(now) 3888 + ", startUpdateCredentialsSession" 3889 + ", " + account.toSafeString() 3890 + ", authTokenType " + authTokenType 3891 + ", loginOptions " + loginOptions; 3892 } 3893 }.bind(); 3894 } finally { 3895 restoreCallingIdentity(identityToken); 3896 } 3897 } 3898 3899 @Override isCredentialsUpdateSuggested( IAccountManagerResponse response, final Account account, final String statusToken)3900 public void isCredentialsUpdateSuggested( 3901 IAccountManagerResponse response, 3902 final Account account, 3903 final String statusToken) { 3904 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3905 Log.v(TAG, 3906 "isCredentialsUpdateSuggested: " + account + ", response " + response 3907 + ", caller's uid " + Binder.getCallingUid() 3908 + ", pid " + Binder.getCallingPid()); 3909 } 3910 if (response == null) { 3911 throw new IllegalArgumentException("response is null"); 3912 } 3913 if (account == null) { 3914 throw new IllegalArgumentException("account is null"); 3915 } 3916 if (TextUtils.isEmpty(statusToken)) { 3917 throw new IllegalArgumentException("status token is empty"); 3918 } 3919 3920 int usrId = UserHandle.getCallingUserId(); 3921 final long identityToken = clearCallingIdentity(); 3922 try { 3923 UserAccounts accounts = getUserAccounts(usrId); 3924 new Session(accounts, response, account.type, false /* expectActivityLaunch */, 3925 false /* stripAuthTokenFromResult */, account.name, 3926 false /* authDetailsRequired */) { 3927 @Override 3928 protected String toDebugString(long now) { 3929 return super.toDebugString(now) + ", isCredentialsUpdateSuggested" 3930 + ", " + account.toSafeString(); 3931 } 3932 3933 @Override 3934 public void run() throws RemoteException { 3935 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken); 3936 } 3937 3938 @Override 3939 public void onResult(Bundle result) { 3940 Bundle.setDefusable(result, true); 3941 IAccountManagerResponse response = getResponseAndClose(); 3942 if (response == null) { 3943 return; 3944 } 3945 3946 if (result == null) { 3947 sendErrorResponse( 3948 response, 3949 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3950 "null bundle"); 3951 return; 3952 } 3953 3954 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3955 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 3956 + response); 3957 } 3958 // Check to see if an error occurred. We know if an error occurred because all 3959 // error codes are greater than 0. 3960 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) { 3961 sendErrorResponse(response, 3962 result.getInt(AccountManager.KEY_ERROR_CODE), 3963 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3964 return; 3965 } 3966 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) { 3967 sendErrorResponse( 3968 response, 3969 AccountManager.ERROR_CODE_INVALID_RESPONSE, 3970 "no result in response"); 3971 return; 3972 } 3973 final Bundle newResult = new Bundle(); 3974 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 3975 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 3976 sendResponse(response, newResult); 3977 } 3978 }.bind(); 3979 } finally { 3980 restoreCallingIdentity(identityToken); 3981 } 3982 } 3983 3984 @Override editProperties(IAccountManagerResponse response, final String accountType, final boolean expectActivityLaunch)3985 public void editProperties(IAccountManagerResponse response, final String accountType, 3986 final boolean expectActivityLaunch) { 3987 final int callingUid = Binder.getCallingUid(); 3988 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3989 Log.v(TAG, "editProperties: accountType " + accountType 3990 + ", response " + response 3991 + ", expectActivityLaunch " + expectActivityLaunch 3992 + ", caller's uid " + callingUid 3993 + ", pid " + Binder.getCallingPid()); 3994 } 3995 if (response == null) throw new IllegalArgumentException("response is null"); 3996 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 3997 int userId = UserHandle.getCallingUserId(); 3998 if (!isAccountManagedByCaller(accountType, callingUid, userId) 3999 && !isSystemUid(callingUid)) { 4000 String msg = String.format( 4001 "uid %s cannot edit authenticator properites for account type: %s", 4002 callingUid, 4003 accountType); 4004 throw new SecurityException(msg); 4005 } 4006 final long identityToken = clearCallingIdentity(); 4007 try { 4008 UserAccounts accounts = getUserAccounts(userId); 4009 new Session(accounts, response, accountType, expectActivityLaunch, 4010 true /* stripAuthTokenFromResult */, null /* accountName */, 4011 false /* authDetailsRequired */) { 4012 @Override 4013 public void run() throws RemoteException { 4014 mAuthenticator.editProperties(this, mAccountType); 4015 } 4016 @Override 4017 protected String toDebugString(long now) { 4018 return super.toDebugString(now) + ", editProperties" 4019 + ", accountType " + accountType; 4020 } 4021 }.bind(); 4022 } finally { 4023 restoreCallingIdentity(identityToken); 4024 } 4025 } 4026 4027 @Override hasAccountAccess(@onNull Account account, @NonNull String packageName, @NonNull UserHandle userHandle)4028 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName, 4029 @NonNull UserHandle userHandle) { 4030 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { 4031 throw new SecurityException("Can be called only by system UID"); 4032 } 4033 Objects.requireNonNull(account, "account cannot be null"); 4034 Objects.requireNonNull(packageName, "packageName cannot be null"); 4035 Objects.requireNonNull(userHandle, "userHandle cannot be null"); 4036 4037 final int userId = userHandle.getIdentifier(); 4038 4039 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 4040 4041 try { 4042 int uid = mPackageManager.getPackageUidAsUser(packageName, userId); 4043 return hasAccountAccess(account, packageName, uid); 4044 } catch (NameNotFoundException e) { 4045 Log.d(TAG, "Package not found " + e.getMessage()); 4046 return false; 4047 } 4048 } 4049 4050 // Returns package with oldest target SDK for given UID. getPackageNameForUid(int uid)4051 private String getPackageNameForUid(int uid) { 4052 String[] packageNames = mPackageManager.getPackagesForUid(uid); 4053 if (ArrayUtils.isEmpty(packageNames)) { 4054 return null; 4055 } 4056 String packageName = packageNames[0]; 4057 if (packageNames.length == 1) { 4058 return packageName; 4059 } 4060 // Due to visibility changes we want to use package with oldest target SDK 4061 int oldestVersion = Integer.MAX_VALUE; 4062 for (String name : packageNames) { 4063 try { 4064 ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(name, 0); 4065 if (applicationInfo != null) { 4066 int version = applicationInfo.targetSdkVersion; 4067 if (version < oldestVersion) { 4068 oldestVersion = version; 4069 packageName = name; 4070 } 4071 } 4072 } catch (NameNotFoundException e) { 4073 // skip 4074 } 4075 } 4076 return packageName; 4077 } 4078 hasAccountAccess(@onNull Account account, @Nullable String packageName, int uid)4079 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName, 4080 int uid) { 4081 if (packageName == null) { 4082 packageName = getPackageNameForUid(uid); 4083 if (packageName == null) { 4084 return false; 4085 } 4086 } 4087 4088 // Use null token which means any token. Having a token means the package 4089 // is trusted by the authenticator, hence it is fine to access the account. 4090 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) { 4091 return true; 4092 } 4093 // In addition to the permissions required to get an auth token we also allow 4094 // the account to be accessed by apps for which user or authenticator granted visibility. 4095 4096 int visibility = resolveAccountVisibility(account, packageName, 4097 getUserAccounts(UserHandle.getUserId(uid))); 4098 return (visibility == AccountManager.VISIBILITY_VISIBLE 4099 || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 4100 } 4101 4102 @Override createRequestAccountAccessIntentSenderAsUser(@onNull Account account, @NonNull String packageName, @NonNull UserHandle userHandle)4103 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account, 4104 @NonNull String packageName, @NonNull UserHandle userHandle) { 4105 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { 4106 throw new SecurityException("Can be called only by system UID"); 4107 } 4108 4109 Objects.requireNonNull(account, "account cannot be null"); 4110 Objects.requireNonNull(packageName, "packageName cannot be null"); 4111 Objects.requireNonNull(userHandle, "userHandle cannot be null"); 4112 4113 final int userId = userHandle.getIdentifier(); 4114 4115 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); 4116 4117 final int uid; 4118 try { 4119 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 4120 } catch (NameNotFoundException e) { 4121 Slog.e(TAG, "Unknown package " + packageName); 4122 return null; 4123 } 4124 4125 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null); 4126 4127 final long identity = Binder.clearCallingIdentity(); 4128 try { 4129 return PendingIntent.getActivityAsUser( 4130 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT 4131 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 4132 null, new UserHandle(userId)).getIntentSender(); 4133 } finally { 4134 Binder.restoreCallingIdentity(identity); 4135 } 4136 } 4137 newRequestAccountAccessIntent(Account account, String packageName, int uid, RemoteCallback callback)4138 private Intent newRequestAccountAccessIntent(Account account, String packageName, 4139 int uid, RemoteCallback callback) { 4140 return newGrantCredentialsPermissionIntent(account, packageName, uid, 4141 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() { 4142 @Override 4143 public void onResult(Bundle value) throws RemoteException { 4144 handleAuthenticatorResponse(true); 4145 } 4146 4147 @Override 4148 public void onRequestContinued() { 4149 /* ignore */ 4150 } 4151 4152 @Override 4153 public void onError(int errorCode, String errorMessage) throws RemoteException { 4154 handleAuthenticatorResponse(false); 4155 } 4156 4157 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { 4158 cancelNotification(getCredentialPermissionNotificationId(account, 4159 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), 4160 UserHandle.getUserHandleForUid(uid)); 4161 if (callback != null) { 4162 Bundle result = new Bundle(); 4163 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted); 4164 callback.sendResult(result); 4165 } 4166 } 4167 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false); 4168 } 4169 4170 @Override 4171 public boolean someUserHasAccount(@NonNull final Account account) { 4172 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) { 4173 throw new SecurityException("Only system can check for accounts across users"); 4174 } 4175 final long token = Binder.clearCallingIdentity(); 4176 try { 4177 AccountAndUser[] allAccounts = getAllAccounts(); 4178 for (int i = allAccounts.length - 1; i >= 0; i--) { 4179 if (allAccounts[i].account.equals(account)) { 4180 return true; 4181 } 4182 } 4183 return false; 4184 } finally { 4185 Binder.restoreCallingIdentity(token); 4186 } 4187 } 4188 4189 private class GetAccountsByTypeAndFeatureSession extends Session { 4190 private final String[] mFeatures; 4191 private volatile Account[] mAccountsOfType = null; 4192 private volatile ArrayList<Account> mAccountsWithFeatures = null; 4193 private volatile int mCurrentAccount = 0; 4194 private final int mCallingUid; 4195 private final String mPackageName; 4196 private final boolean mIncludeManagedNotVisible; 4197 4198 public GetAccountsByTypeAndFeatureSession( 4199 UserAccounts accounts, 4200 IAccountManagerResponse response, 4201 String type, 4202 String[] features, 4203 int callingUid, 4204 String packageName, 4205 boolean includeManagedNotVisible) { 4206 super(accounts, response, type, false /* expectActivityLaunch */, 4207 true /* stripAuthTokenFromResult */, null /* accountName */, 4208 false /* authDetailsRequired */); 4209 mCallingUid = callingUid; 4210 mFeatures = features; 4211 mPackageName = packageName; 4212 mIncludeManagedNotVisible = includeManagedNotVisible; 4213 } 4214 4215 @Override 4216 public void run() throws RemoteException { 4217 mAccountsOfType = getAccountsFromCache(mAccounts, mAccountType, 4218 mCallingUid, mPackageName, mIncludeManagedNotVisible); 4219 // check whether each account matches the requested features 4220 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length); 4221 mCurrentAccount = 0; 4222 4223 checkAccount(); 4224 } 4225 4226 public void checkAccount() { 4227 if (mCurrentAccount >= mAccountsOfType.length) { 4228 sendResult(); 4229 return; 4230 } 4231 4232 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 4233 if (accountAuthenticator == null) { 4234 // It is possible that the authenticator has died, which is indicated by 4235 // mAuthenticator being set to null. If this happens then just abort. 4236 // There is no need to send back a result or error in this case since 4237 // that already happened when mAuthenticator was cleared. 4238 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4239 Log.v(TAG, "checkAccount: aborting session since we are no longer" 4240 + " connected to the authenticator, " + toDebugString()); 4241 } 4242 return; 4243 } 4244 try { 4245 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 4246 } catch (RemoteException e) { 4247 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 4248 } 4249 } 4250 4251 @Override 4252 public void onResult(Bundle result) { 4253 Bundle.setDefusable(result, true); 4254 mNumResults++; 4255 if (result == null) { 4256 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 4257 return; 4258 } 4259 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 4260 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 4261 } 4262 mCurrentAccount++; 4263 checkAccount(); 4264 } 4265 4266 public void sendResult() { 4267 IAccountManagerResponse response = getResponseAndClose(); 4268 if (response != null) { 4269 try { 4270 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 4271 for (int i = 0; i < accounts.length; i++) { 4272 accounts[i] = mAccountsWithFeatures.get(i); 4273 } 4274 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4275 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 4276 + response); 4277 } 4278 Bundle result = new Bundle(); 4279 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 4280 response.onResult(result); 4281 } catch (RemoteException e) { 4282 // if the caller is dead then there is no one to care about remote exceptions 4283 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4284 Log.v(TAG, "failure while notifying response", e); 4285 } 4286 } 4287 } 4288 } 4289 4290 @Override 4291 protected String toDebugString(long now) { 4292 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 4293 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 4294 } 4295 } 4296 4297 /** 4298 * Returns the accounts visible to the client within the context of a specific user 4299 * @hide 4300 */ 4301 @NonNull 4302 public Account[] getAccounts(int userId, String opPackageName) { 4303 int callingUid = Binder.getCallingUid(); 4304 mAppOpsManager.checkPackage(callingUid, opPackageName); 4305 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 4306 opPackageName); 4307 if (visibleAccountTypes.isEmpty()) { 4308 return EMPTY_ACCOUNT_ARRAY; 4309 } 4310 final long identityToken = clearCallingIdentity(); 4311 try { 4312 UserAccounts accounts = getUserAccounts(userId); 4313 return getAccountsInternal( 4314 accounts, 4315 callingUid, 4316 opPackageName, 4317 visibleAccountTypes, 4318 false /* includeUserManagedNotVisible */); 4319 } finally { 4320 restoreCallingIdentity(identityToken); 4321 } 4322 } 4323 4324 /** 4325 * Returns accounts for all running users, ignores visibility values. 4326 * 4327 * @hide 4328 */ 4329 @NonNull 4330 public AccountAndUser[] getRunningAccounts() { 4331 final int[] runningUserIds; 4332 try { 4333 runningUserIds = ActivityManager.getService().getRunningUserIds(); 4334 } catch (RemoteException e) { 4335 // Running in system_server; should never happen 4336 throw new RuntimeException(e); 4337 } 4338 return getAccounts(runningUserIds); 4339 } 4340 4341 /** 4342 * Returns accounts for all users, ignores visibility values. 4343 * 4344 * @hide 4345 */ 4346 @NonNull 4347 public AccountAndUser[] getAllAccounts() { 4348 final List<UserInfo> users = getUserManager().getAliveUsers(); 4349 final int[] userIds = new int[users.size()]; 4350 for (int i = 0; i < userIds.length; i++) { 4351 userIds[i] = users.get(i).id; 4352 } 4353 return getAccounts(userIds); 4354 } 4355 4356 @NonNull 4357 private AccountAndUser[] getAccounts(int[] userIds) { 4358 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 4359 for (int userId : userIds) { 4360 UserAccounts userAccounts = getUserAccounts(userId); 4361 if (userAccounts == null) continue; 4362 Account[] accounts = getAccountsFromCache( 4363 userAccounts, 4364 null /* type */, 4365 Binder.getCallingUid(), 4366 null /* packageName */, 4367 false /* include managed not visible*/); 4368 for (Account account : accounts) { 4369 runningAccounts.add(new AccountAndUser(account, userId)); 4370 } 4371 } 4372 4373 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 4374 return runningAccounts.toArray(accountsArray); 4375 } 4376 4377 @Override 4378 @NonNull 4379 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { 4380 int callingUid = Binder.getCallingUid(); 4381 mAppOpsManager.checkPackage(callingUid, opPackageName); 4382 return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1, 4383 opPackageName, false /* includeUserManagedNotVisible */); 4384 } 4385 4386 @NonNull 4387 private Account[] getAccountsAsUserForPackage( 4388 String type, 4389 int userId, 4390 String callingPackage, 4391 int packageUid, 4392 String opPackageName, 4393 boolean includeUserManagedNotVisible) { 4394 int callingUid = Binder.getCallingUid(); 4395 // Only allow the system process to read accounts of other users 4396 if (userId != UserHandle.getCallingUserId() 4397 && callingUid != Process.SYSTEM_UID 4398 && mContext.checkCallingOrSelfPermission( 4399 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 4400 != PackageManager.PERMISSION_GRANTED) { 4401 throw new SecurityException("User " + UserHandle.getCallingUserId() 4402 + " trying to get account for " + userId); 4403 } 4404 4405 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4406 Log.v(TAG, "getAccounts: accountType " + type 4407 + ", caller's uid " + Binder.getCallingUid() 4408 + ", pid " + Binder.getCallingPid()); 4409 } 4410 4411 // If the original calling app was using account choosing activity 4412 // provided by the framework or authenticator we'll passing in 4413 // the original caller's uid here, which is what should be used for filtering. 4414 List<String> managedTypes = 4415 getTypesManagedByCaller(callingUid, UserHandle.getUserId(callingUid)); 4416 if (packageUid != -1 && 4417 ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) 4418 || (type != null && managedTypes.contains(type))))) { 4419 callingUid = packageUid; 4420 opPackageName = callingPackage; 4421 } 4422 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 4423 opPackageName); 4424 if (visibleAccountTypes.isEmpty() 4425 || (type != null && !visibleAccountTypes.contains(type))) { 4426 return EMPTY_ACCOUNT_ARRAY; 4427 } else if (visibleAccountTypes.contains(type)) { 4428 // Prune the list down to just the requested type. 4429 visibleAccountTypes = new ArrayList<>(); 4430 visibleAccountTypes.add(type); 4431 } // else aggregate all the visible accounts (it won't matter if the 4432 // list is empty). 4433 4434 final long identityToken = clearCallingIdentity(); 4435 try { 4436 UserAccounts accounts = getUserAccounts(userId); 4437 return getAccountsInternal( 4438 accounts, 4439 callingUid, 4440 opPackageName, 4441 visibleAccountTypes, 4442 includeUserManagedNotVisible); 4443 } finally { 4444 restoreCallingIdentity(identityToken); 4445 } 4446 } 4447 4448 @NonNull 4449 private Account[] getAccountsInternal( 4450 UserAccounts userAccounts, 4451 int callingUid, 4452 String callingPackage, 4453 List<String> visibleAccountTypes, 4454 boolean includeUserManagedNotVisible) { 4455 ArrayList<Account> visibleAccounts = new ArrayList<>(); 4456 for (String visibleType : visibleAccountTypes) { 4457 Account[] accountsForType = getAccountsFromCache( 4458 userAccounts, visibleType, callingUid, callingPackage, 4459 includeUserManagedNotVisible); 4460 if (accountsForType != null) { 4461 visibleAccounts.addAll(Arrays.asList(accountsForType)); 4462 } 4463 } 4464 Account[] result = new Account[visibleAccounts.size()]; 4465 for (int i = 0; i < visibleAccounts.size(); i++) { 4466 result[i] = visibleAccounts.get(i); 4467 } 4468 return result; 4469 } 4470 4471 @Override 4472 public void addSharedAccountsFromParentUser(int parentUserId, int userId, 4473 String opPackageName) { 4474 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser"); 4475 Account[] accounts = getAccountsAsUser(null, parentUserId, opPackageName); 4476 for (Account account : accounts) { 4477 addSharedAccountAsUser(account, userId); 4478 } 4479 } 4480 4481 private boolean addSharedAccountAsUser(Account account, int userId) { 4482 userId = handleIncomingUser(userId); 4483 UserAccounts accounts = getUserAccounts(userId); 4484 accounts.accountsDb.deleteSharedAccount(account); 4485 long accountId = accounts.accountsDb.insertSharedAccount(account); 4486 if (accountId < 0) { 4487 Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() 4488 + ", skipping the DB insert failed"); 4489 return false; 4490 } 4491 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_SHARED_ACCOUNTS, accountId, 4492 accounts); 4493 return true; 4494 } 4495 4496 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 4497 userId = handleIncomingUser(userId); 4498 UserAccounts accounts = getUserAccounts(userId); 4499 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account); 4500 int r = accounts.accountsDb.renameSharedAccount(account, newName); 4501 if (r > 0) { 4502 int callingUid = getCallingUid(); 4503 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS, 4504 sharedTableAccountId, accounts, callingUid); 4505 // Recursively rename the account. 4506 renameAccountInternal(accounts, account, newName); 4507 } 4508 return r > 0; 4509 } 4510 4511 public boolean removeSharedAccountAsUser(Account account, int userId) { 4512 return removeSharedAccountAsUser(account, userId, getCallingUid()); 4513 } 4514 4515 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) { 4516 userId = handleIncomingUser(userId); 4517 UserAccounts accounts = getUserAccounts(userId); 4518 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account); 4519 boolean deleted = accounts.accountsDb.deleteSharedAccount(account); 4520 if (deleted) { 4521 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS, 4522 sharedTableAccountId, accounts, callingUid); 4523 removeAccountInternal(accounts, account, callingUid); 4524 } 4525 return deleted; 4526 } 4527 4528 public Account[] getSharedAccountsAsUser(int userId) { 4529 userId = handleIncomingUser(userId); 4530 UserAccounts accounts = getUserAccounts(userId); 4531 synchronized (accounts.dbLock) { 4532 List<Account> accountList = accounts.accountsDb.getSharedAccounts(); 4533 Account[] accountArray = new Account[accountList.size()]; 4534 accountList.toArray(accountArray); 4535 return accountArray; 4536 } 4537 } 4538 4539 @Override 4540 @NonNull 4541 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { 4542 int callingUid = Binder.getCallingUid(); 4543 if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) { 4544 // Don't do opPackageName check - caller is system. 4545 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 4546 + callingUid + " with uid=" + uid); 4547 } 4548 return getAccountsAsUserForPackage(null, UserHandle.getCallingUserId(), packageName, uid, 4549 opPackageName, true /* includeUserManagedNotVisible */); 4550 } 4551 4552 @Override 4553 @NonNull 4554 public Account[] getAccountsByTypeForPackage(String type, String packageName, 4555 String opPackageName) { 4556 int callingUid = Binder.getCallingUid(); 4557 int userId = UserHandle.getCallingUserId(); 4558 mAppOpsManager.checkPackage(callingUid, opPackageName); 4559 int packageUid = -1; 4560 try { 4561 packageUid = mPackageManager.getPackageUidAsUser(packageName, userId); 4562 } catch (NameNotFoundException re) { 4563 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 4564 return EMPTY_ACCOUNT_ARRAY; 4565 } 4566 if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) 4567 && (type != null && !isAccountManagedByCaller(type, callingUid, userId))) { 4568 return EMPTY_ACCOUNT_ARRAY; 4569 } 4570 if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && type == null) { 4571 return getAccountsAsUserForPackage(type, userId, 4572 packageName, packageUid, opPackageName, false /* includeUserManagedNotVisible */); 4573 } 4574 return getAccountsAsUserForPackage(type, userId, 4575 packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */); 4576 } 4577 4578 private boolean needToStartChooseAccountActivity(Account[] accounts, String callingPackage) { 4579 if (accounts.length < 1) return false; 4580 if (accounts.length > 1) return true; 4581 Account account = accounts[0]; 4582 UserAccounts userAccounts = getUserAccounts(UserHandle.getCallingUserId()); 4583 int visibility = resolveAccountVisibility(account, callingPackage, userAccounts); 4584 if (visibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) return true; 4585 return false; 4586 } 4587 4588 private void startChooseAccountActivityWithAccounts( 4589 IAccountManagerResponse response, Account[] accounts, String callingPackage) { 4590 Intent intent = new Intent(mContext, ChooseAccountActivity.class); 4591 intent.putExtra(AccountManager.KEY_ACCOUNTS, accounts); 4592 intent.putExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE, 4593 new AccountManagerResponse(response)); 4594 intent.putExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME, callingPackage); 4595 4596 mContext.startActivityAsUser(intent, UserHandle.of(UserHandle.getCallingUserId())); 4597 } 4598 4599 private void handleGetAccountsResult( 4600 IAccountManagerResponse response, 4601 Account[] accounts, 4602 String callingPackage) { 4603 4604 if (needToStartChooseAccountActivity(accounts, callingPackage)) { 4605 startChooseAccountActivityWithAccounts(response, accounts, callingPackage); 4606 return; 4607 } 4608 if (accounts.length == 1) { 4609 Bundle bundle = new Bundle(); 4610 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accounts[0].name); 4611 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accounts[0].type); 4612 onResult(response, bundle); 4613 return; 4614 } 4615 // No qualified account exists, return an empty Bundle. 4616 onResult(response, new Bundle()); 4617 } 4618 4619 @Override 4620 public void getAccountByTypeAndFeatures( 4621 IAccountManagerResponse response, 4622 String accountType, 4623 String[] features, 4624 String opPackageName) { 4625 4626 int callingUid = Binder.getCallingUid(); 4627 mAppOpsManager.checkPackage(callingUid, opPackageName); 4628 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4629 Log.v(TAG, "getAccount: accountType " + accountType 4630 + ", response " + response 4631 + ", features " + Arrays.toString(features) 4632 + ", caller's uid " + callingUid 4633 + ", pid " + Binder.getCallingPid()); 4634 } 4635 if (response == null) throw new IllegalArgumentException("response is null"); 4636 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 4637 4638 int userId = UserHandle.getCallingUserId(); 4639 4640 final long identityToken = clearCallingIdentity(); 4641 try { 4642 UserAccounts userAccounts = getUserAccounts(userId); 4643 if (ArrayUtils.isEmpty(features)) { 4644 Account[] accountsWithManagedNotVisible = getAccountsFromCache( 4645 userAccounts, accountType, callingUid, opPackageName, 4646 true /* include managed not visible */); 4647 handleGetAccountsResult( 4648 response, accountsWithManagedNotVisible, opPackageName); 4649 return; 4650 } 4651 4652 IAccountManagerResponse retrieveAccountsResponse = 4653 new IAccountManagerResponse.Stub() { 4654 @Override 4655 public void onResult(Bundle value) throws RemoteException { 4656 Parcelable[] parcelables = value.getParcelableArray( 4657 AccountManager.KEY_ACCOUNTS); 4658 Account[] accounts = new Account[parcelables.length]; 4659 for (int i = 0; i < parcelables.length; i++) { 4660 accounts[i] = (Account) parcelables[i]; 4661 } 4662 handleGetAccountsResult( 4663 response, accounts, opPackageName); 4664 } 4665 4666 @Override 4667 public void onError(int errorCode, String errorMessage) 4668 throws RemoteException { 4669 // Will not be called in this case. 4670 } 4671 }; 4672 new GetAccountsByTypeAndFeatureSession( 4673 userAccounts, 4674 retrieveAccountsResponse, 4675 accountType, 4676 features, 4677 callingUid, 4678 opPackageName, 4679 true /* include managed not visible */).bind(); 4680 } finally { 4681 restoreCallingIdentity(identityToken); 4682 } 4683 } 4684 4685 @Override 4686 public void getAccountsByFeatures( 4687 IAccountManagerResponse response, 4688 String type, 4689 String[] features, 4690 String opPackageName) { 4691 int callingUid = Binder.getCallingUid(); 4692 mAppOpsManager.checkPackage(callingUid, opPackageName); 4693 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4694 Log.v(TAG, "getAccounts: accountType " + type 4695 + ", response " + response 4696 + ", features " + Arrays.toString(features) 4697 + ", caller's uid " + callingUid 4698 + ", pid " + Binder.getCallingPid()); 4699 } 4700 if (response == null) throw new IllegalArgumentException("response is null"); 4701 if (type == null) throw new IllegalArgumentException("accountType is null"); 4702 int userId = UserHandle.getCallingUserId(); 4703 4704 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 4705 opPackageName); 4706 if (!visibleAccountTypes.contains(type)) { 4707 Bundle result = new Bundle(); 4708 // Need to return just the accounts that are from matching signatures. 4709 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, EMPTY_ACCOUNT_ARRAY); 4710 try { 4711 response.onResult(result); 4712 } catch (RemoteException e) { 4713 Log.e(TAG, "Cannot respond to caller do to exception." , e); 4714 } 4715 return; 4716 } 4717 4718 final long identityToken = clearCallingIdentity(); 4719 try { 4720 UserAccounts userAccounts = getUserAccounts(userId); 4721 if (features == null || features.length == 0) { 4722 Account[] accounts = getAccountsFromCache(userAccounts, type, callingUid, 4723 opPackageName, false); 4724 Bundle result = new Bundle(); 4725 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 4726 onResult(response, result); 4727 return; 4728 } 4729 new GetAccountsByTypeAndFeatureSession( 4730 userAccounts, 4731 response, 4732 type, 4733 features, 4734 callingUid, 4735 opPackageName, 4736 false /* include managed not visible */).bind(); 4737 } finally { 4738 restoreCallingIdentity(identityToken); 4739 } 4740 } 4741 4742 @Override 4743 public void onAccountAccessed(String token) throws RemoteException { 4744 final int uid = Binder.getCallingUid(); 4745 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { 4746 return; 4747 } 4748 final int userId = UserHandle.getCallingUserId(); 4749 final long identity = Binder.clearCallingIdentity(); 4750 try { 4751 for (Account account : getAccounts(userId, mContext.getOpPackageName())) { 4752 if (Objects.equals(account.getAccessId(), token)) { 4753 // An app just accessed the account. At this point it knows about 4754 // it and there is not need to hide this account from the app. 4755 // Do we need to update account visibility here? 4756 if (!hasAccountAccess(account, null, uid)) { 4757 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, 4758 uid, true); 4759 } 4760 } 4761 } 4762 } finally { 4763 Binder.restoreCallingIdentity(identity); 4764 } 4765 } 4766 4767 @Override 4768 public void onShellCommand(FileDescriptor in, FileDescriptor out, 4769 FileDescriptor err, String[] args, ShellCallback callback, 4770 ResultReceiver resultReceiver) { 4771 new AccountManagerServiceShellCommand(this).exec(this, in, out, err, args, 4772 callback, resultReceiver); 4773 } 4774 4775 private abstract class Session extends IAccountAuthenticatorResponse.Stub 4776 implements IBinder.DeathRecipient, ServiceConnection { 4777 IAccountManagerResponse mResponse; 4778 final String mAccountType; 4779 final boolean mExpectActivityLaunch; 4780 final long mCreationTime; 4781 final String mAccountName; 4782 // Indicates if we need to add auth details(like last credential time) 4783 final boolean mAuthDetailsRequired; 4784 // If set, we need to update the last authenticated time. This is 4785 // currently 4786 // used on 4787 // successful confirming credentials. 4788 final boolean mUpdateLastAuthenticatedTime; 4789 4790 public int mNumResults = 0; 4791 private int mNumRequestContinued = 0; 4792 private int mNumErrors = 0; 4793 4794 IAccountAuthenticator mAuthenticator = null; 4795 4796 private final boolean mStripAuthTokenFromResult; 4797 protected final UserAccounts mAccounts; 4798 4799 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4800 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4801 boolean authDetailsRequired) { 4802 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult, 4803 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */); 4804 } 4805 4806 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 4807 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 4808 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) { 4809 super(); 4810 //if (response == null) throw new IllegalArgumentException("response is null"); 4811 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 4812 mAccounts = accounts; 4813 mStripAuthTokenFromResult = stripAuthTokenFromResult; 4814 mResponse = response; 4815 mAccountType = accountType; 4816 mExpectActivityLaunch = expectActivityLaunch; 4817 mCreationTime = SystemClock.elapsedRealtime(); 4818 mAccountName = accountName; 4819 mAuthDetailsRequired = authDetailsRequired; 4820 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; 4821 4822 synchronized (mSessions) { 4823 mSessions.put(toString(), this); 4824 } 4825 if (response != null) { 4826 try { 4827 response.asBinder().linkToDeath(this, 0 /* flags */); 4828 } catch (RemoteException e) { 4829 mResponse = null; 4830 binderDied(); 4831 } 4832 } 4833 } 4834 4835 IAccountManagerResponse getResponseAndClose() { 4836 if (mResponse == null) { 4837 close(); 4838 return null; 4839 } 4840 IAccountManagerResponse response = mResponse; 4841 close(); // this clears mResponse so we need to save the response before this call 4842 return response; 4843 } 4844 4845 /** 4846 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our 4847 * security policy. 4848 * 4849 * In particular we want to make sure that the Authenticator doesn't try to trick users 4850 * into launching arbitrary intents on the device via by tricking to click authenticator 4851 * supplied entries in the system Settings app. 4852 */ 4853 protected boolean checkKeyIntent(int authUid, Intent intent) { 4854 // Explicitly set an empty ClipData to ensure that we don't offer to 4855 // promote any Uris contained inside for granting purposes 4856 if (intent.getClipData() == null) { 4857 intent.setClipData(ClipData.newPlainText(null, null)); 4858 } 4859 intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION 4860 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION 4861 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION 4862 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); 4863 final long bid = Binder.clearCallingIdentity(); 4864 try { 4865 PackageManager pm = mContext.getPackageManager(); 4866 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 4867 if (resolveInfo == null) { 4868 return false; 4869 } 4870 ActivityInfo targetActivityInfo = resolveInfo.activityInfo; 4871 int targetUid = targetActivityInfo.applicationInfo.uid; 4872 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 4873 if (!isExportedSystemActivity(targetActivityInfo) 4874 && !pmi.hasSignatureCapability( 4875 targetUid, authUid, 4876 PackageParser.SigningDetails.CertCapabilities.AUTH)) { 4877 String pkgName = targetActivityInfo.packageName; 4878 String activityName = targetActivityInfo.name; 4879 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that " 4880 + "does not share a signature with the supplying authenticator (%s)."; 4881 Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType)); 4882 return false; 4883 } 4884 return true; 4885 } finally { 4886 Binder.restoreCallingIdentity(bid); 4887 } 4888 } 4889 4890 private boolean isExportedSystemActivity(ActivityInfo activityInfo) { 4891 String className = activityInfo.name; 4892 return "android".equals(activityInfo.packageName) && 4893 (GrantCredentialsPermissionActivity.class.getName().equals(className) 4894 || CantAddAccountActivity.class.getName().equals(className)); 4895 } 4896 4897 private void close() { 4898 synchronized (mSessions) { 4899 if (mSessions.remove(toString()) == null) { 4900 // the session was already closed, so bail out now 4901 return; 4902 } 4903 } 4904 if (mResponse != null) { 4905 // stop listening for response deaths 4906 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 4907 4908 // clear this so that we don't accidentally send any further results 4909 mResponse = null; 4910 } 4911 cancelTimeout(); 4912 unbind(); 4913 } 4914 4915 @Override 4916 public void binderDied() { 4917 mResponse = null; 4918 close(); 4919 } 4920 4921 protected String toDebugString() { 4922 return toDebugString(SystemClock.elapsedRealtime()); 4923 } 4924 4925 protected String toDebugString(long now) { 4926 return "Session: expectLaunch " + mExpectActivityLaunch 4927 + ", connected " + (mAuthenticator != null) 4928 + ", stats (" + mNumResults + "/" + mNumRequestContinued 4929 + "/" + mNumErrors + ")" 4930 + ", lifetime " + ((now - mCreationTime) / 1000.0); 4931 } 4932 4933 void bind() { 4934 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4935 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 4936 } 4937 if (!bindToAuthenticator(mAccountType)) { 4938 Log.d(TAG, "bind attempt failed for " + toDebugString()); 4939 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 4940 } 4941 } 4942 4943 private void unbind() { 4944 if (mAuthenticator != null) { 4945 mAuthenticator = null; 4946 mContext.unbindService(this); 4947 } 4948 } 4949 4950 public void cancelTimeout() { 4951 mHandler.removeMessages(MESSAGE_TIMED_OUT, this); 4952 } 4953 4954 @Override 4955 public void onServiceConnected(ComponentName name, IBinder service) { 4956 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 4957 try { 4958 run(); 4959 } catch (RemoteException e) { 4960 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4961 "remote exception"); 4962 } 4963 } 4964 4965 @Override 4966 public void onServiceDisconnected(ComponentName name) { 4967 mAuthenticator = null; 4968 IAccountManagerResponse response = getResponseAndClose(); 4969 if (response != null) { 4970 try { 4971 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4972 "disconnected"); 4973 } catch (RemoteException e) { 4974 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4975 Log.v(TAG, "Session.onServiceDisconnected: " 4976 + "caught RemoteException while responding", e); 4977 } 4978 } 4979 } 4980 } 4981 4982 public abstract void run() throws RemoteException; 4983 4984 public void onTimedOut() { 4985 IAccountManagerResponse response = getResponseAndClose(); 4986 if (response != null) { 4987 try { 4988 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 4989 "timeout"); 4990 } catch (RemoteException e) { 4991 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4992 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 4993 e); 4994 } 4995 } 4996 } 4997 } 4998 4999 @Override 5000 public void onResult(Bundle result) { 5001 Bundle.setDefusable(result, true); 5002 mNumResults++; 5003 Intent intent = null; 5004 if (result != null) { 5005 boolean isSuccessfulConfirmCreds = result.getBoolean( 5006 AccountManager.KEY_BOOLEAN_RESULT, false); 5007 boolean isSuccessfulUpdateCredsOrAddAccount = 5008 result.containsKey(AccountManager.KEY_ACCOUNT_NAME) 5009 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE); 5010 // We should only update lastAuthenticated time, if 5011 // mUpdateLastAuthenticatedTime is true and the confirmRequest 5012 // or updateRequest was successful 5013 boolean needUpdate = mUpdateLastAuthenticatedTime 5014 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount); 5015 if (needUpdate || mAuthDetailsRequired) { 5016 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType); 5017 if (needUpdate && accountPresent) { 5018 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType)); 5019 } 5020 if (mAuthDetailsRequired) { 5021 long lastAuthenticatedTime = -1; 5022 if (accountPresent) { 5023 lastAuthenticatedTime = mAccounts.accountsDb 5024 .findAccountLastAuthenticatedTime( 5025 new Account(mAccountName, mAccountType)); 5026 } 5027 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME, 5028 lastAuthenticatedTime); 5029 } 5030 } 5031 } 5032 if (result != null 5033 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 5034 if (!checkKeyIntent( 5035 Binder.getCallingUid(), 5036 intent)) { 5037 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 5038 "invalid intent in bundle returned"); 5039 return; 5040 } 5041 } 5042 if (result != null 5043 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 5044 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 5045 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 5046 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 5047 Account account = new Account(accountName, accountType); 5048 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 5049 new UserHandle(mAccounts.userId)); 5050 } 5051 } 5052 IAccountManagerResponse response; 5053 if (mExpectActivityLaunch && result != null 5054 && result.containsKey(AccountManager.KEY_INTENT)) { 5055 response = mResponse; 5056 } else { 5057 response = getResponseAndClose(); 5058 } 5059 if (response != null) { 5060 try { 5061 if (result == null) { 5062 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5063 Log.v(TAG, getClass().getSimpleName() 5064 + " calling onError() on response " + response); 5065 } 5066 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 5067 "null bundle returned"); 5068 } else { 5069 if (mStripAuthTokenFromResult) { 5070 result.remove(AccountManager.KEY_AUTHTOKEN); 5071 } 5072 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5073 Log.v(TAG, getClass().getSimpleName() 5074 + " calling onResult() on response " + response); 5075 } 5076 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 5077 (intent == null)) { 5078 // All AccountManager error codes are greater than 0 5079 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 5080 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 5081 } else { 5082 response.onResult(result); 5083 } 5084 } 5085 } catch (RemoteException e) { 5086 // if the caller is dead then there is no one to care about remote exceptions 5087 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5088 Log.v(TAG, "failure while notifying response", e); 5089 } 5090 } 5091 } 5092 } 5093 5094 @Override 5095 public void onRequestContinued() { 5096 mNumRequestContinued++; 5097 } 5098 5099 @Override 5100 public void onError(int errorCode, String errorMessage) { 5101 mNumErrors++; 5102 IAccountManagerResponse response = getResponseAndClose(); 5103 if (response != null) { 5104 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5105 Log.v(TAG, getClass().getSimpleName() 5106 + " calling onError() on response " + response); 5107 } 5108 try { 5109 response.onError(errorCode, errorMessage); 5110 } catch (RemoteException e) { 5111 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5112 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 5113 } 5114 } 5115 } else { 5116 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5117 Log.v(TAG, "Session.onError: already closed"); 5118 } 5119 } 5120 } 5121 5122 /** 5123 * find the component name for the authenticator and initiate a bind 5124 * if no authenticator or the bind fails then return false, otherwise return true 5125 */ 5126 private boolean bindToAuthenticator(String authenticatorType) { 5127 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 5128 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 5129 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 5130 if (authenticatorInfo == null) { 5131 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5132 Log.v(TAG, "there is no authenticator for " + authenticatorType 5133 + ", bailing out"); 5134 } 5135 return false; 5136 } 5137 5138 if (!isLocalUnlockedUser(mAccounts.userId) 5139 && !authenticatorInfo.componentInfo.directBootAware) { 5140 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName 5141 + " which isn't encryption aware"); 5142 return false; 5143 } 5144 5145 Intent intent = new Intent(); 5146 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 5147 intent.setComponent(authenticatorInfo.componentName); 5148 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5149 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 5150 } 5151 int flags = Context.BIND_AUTO_CREATE; 5152 if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) { 5153 flags |= Context.BIND_ALLOW_INSTANT; 5154 } 5155 if (!mContext.bindServiceAsUser(intent, this, flags, UserHandle.of(mAccounts.userId))) { 5156 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5157 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 5158 } 5159 return false; 5160 } 5161 5162 return true; 5163 } 5164 } 5165 5166 class MessageHandler extends Handler { 5167 MessageHandler(Looper looper) { 5168 super(looper); 5169 } 5170 5171 @Override 5172 public void handleMessage(Message msg) { 5173 switch (msg.what) { 5174 case MESSAGE_TIMED_OUT: 5175 Session session = (Session)msg.obj; 5176 session.onTimedOut(); 5177 break; 5178 5179 case MESSAGE_COPY_SHARED_ACCOUNT: 5180 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); 5181 break; 5182 5183 default: 5184 throw new IllegalStateException("unhandled message: " + msg.what); 5185 } 5186 } 5187 } 5188 5189 private void logRecord(UserAccounts accounts, String action, String tableName) { 5190 logRecord(action, tableName, -1, accounts); 5191 } 5192 5193 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) { 5194 logRecord(action, tableName, -1, accounts, uid); 5195 } 5196 5197 /* 5198 * This function receives an opened writable database. 5199 */ 5200 private void logRecord(String action, String tableName, long accountId, 5201 UserAccounts userAccount) { 5202 logRecord(action, tableName, accountId, userAccount, getCallingUid()); 5203 } 5204 5205 /* 5206 * This function receives an opened writable database and writes to it in a separate thread. 5207 */ 5208 private void logRecord(String action, String tableName, long accountId, 5209 UserAccounts userAccount, int callingUid) { 5210 5211 class LogRecordTask implements Runnable { 5212 private final String action; 5213 private final String tableName; 5214 private final long accountId; 5215 private final UserAccounts userAccount; 5216 private final int callingUid; 5217 private final long userDebugDbInsertionPoint; 5218 5219 LogRecordTask(final String action, 5220 final String tableName, 5221 final long accountId, 5222 final UserAccounts userAccount, 5223 final int callingUid, 5224 final long userDebugDbInsertionPoint) { 5225 this.action = action; 5226 this.tableName = tableName; 5227 this.accountId = accountId; 5228 this.userAccount = userAccount; 5229 this.callingUid = callingUid; 5230 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint; 5231 } 5232 5233 @Override 5234 public void run() { 5235 synchronized (userAccount.accountsDb.mDebugStatementLock) { 5236 SQLiteStatement logStatement = userAccount.accountsDb.getStatementForLogging(); 5237 if (logStatement == null) { 5238 return; // Can't log. 5239 } 5240 logStatement.bindLong(1, accountId); 5241 logStatement.bindString(2, action); 5242 logStatement.bindString(3, mDateFormat.format(new Date())); 5243 logStatement.bindLong(4, callingUid); 5244 logStatement.bindString(5, tableName); 5245 logStatement.bindLong(6, userDebugDbInsertionPoint); 5246 try { 5247 logStatement.execute(); 5248 } catch (IllegalStateException e) { 5249 // Guard against crash, DB can already be closed 5250 // since this statement is executed on a handler thread 5251 Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId 5252 + " action=" + action + " tableName=" + tableName + " Error: " + e); 5253 } finally { 5254 logStatement.clearBindings(); 5255 } 5256 } 5257 } 5258 } 5259 long insertionPoint = userAccount.accountsDb.reserveDebugDbInsertionPoint(); 5260 if (insertionPoint != -1) { 5261 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount, 5262 callingUid, insertionPoint); 5263 mHandler.post(logTask); 5264 } 5265 } 5266 5267 public IBinder onBind(@SuppressWarnings("unused") Intent intent) { 5268 return asBinder(); 5269 } 5270 5271 /** 5272 * Searches array of arguments for the specified string 5273 * @param args array of argument strings 5274 * @param value value to search for 5275 * @return true if the value is contained in the array 5276 */ 5277 private static boolean scanArgs(String[] args, String value) { 5278 if (args != null) { 5279 for (String arg : args) { 5280 if (value.equals(arg)) { 5281 return true; 5282 } 5283 } 5284 } 5285 return false; 5286 } 5287 5288 @Override 5289 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 5290 if (!DumpUtils.checkDumpPermission(mContext, TAG, fout)) return; 5291 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 5292 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 5293 5294 final List<UserInfo> users = getUserManager().getUsers(); 5295 for (UserInfo user : users) { 5296 ipw.println("User " + user + ":"); 5297 ipw.increaseIndent(); 5298 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 5299 ipw.println(); 5300 ipw.decreaseIndent(); 5301 } 5302 } 5303 5304 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 5305 String[] args, boolean isCheckinRequest) { 5306 if (isCheckinRequest) { 5307 // This is a checkin request. *Only* upload the account types and the count of 5308 // each. 5309 synchronized (userAccounts.dbLock) { 5310 userAccounts.accountsDb.dumpDeAccountsTable(fout); 5311 } 5312 } else { 5313 Account[] accounts = getAccountsFromCache(userAccounts, null /* type */, 5314 Process.SYSTEM_UID, null /* packageName */, false); 5315 fout.println("Accounts: " + accounts.length); 5316 for (Account account : accounts) { 5317 fout.println(" " + account.toString()); 5318 } 5319 5320 // Add debug information. 5321 fout.println(); 5322 synchronized (userAccounts.dbLock) { 5323 userAccounts.accountsDb.dumpDebugTable(fout); 5324 } 5325 fout.println(); 5326 synchronized (mSessions) { 5327 final long now = SystemClock.elapsedRealtime(); 5328 fout.println("Active Sessions: " + mSessions.size()); 5329 for (Session session : mSessions.values()) { 5330 fout.println(" " + session.toDebugString(now)); 5331 } 5332 } 5333 5334 fout.println(); 5335 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 5336 5337 boolean isUserUnlocked; 5338 synchronized (mUsers) { 5339 isUserUnlocked = isLocalUnlockedUser(userAccounts.userId); 5340 } 5341 // Following logs are printed only when user is unlocked. 5342 if (!isUserUnlocked) { 5343 return; 5344 } 5345 fout.println(); 5346 synchronized (userAccounts.dbLock) { 5347 Map<Account, Map<String, Integer>> allVisibilityValues = 5348 userAccounts.accountsDb.findAllVisibilityValues(); 5349 fout.println("Account visibility:"); 5350 for (Account account : allVisibilityValues.keySet()) { 5351 fout.println(" " + account.name); 5352 Map<String, Integer> visibilities = allVisibilityValues.get(account); 5353 for (Entry<String, Integer> entry : visibilities.entrySet()) { 5354 fout.println(" " + entry.getKey() + ", " + entry.getValue()); 5355 } 5356 } 5357 } 5358 } 5359 } 5360 5361 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 5362 Intent intent, String packageName, final int userId) { 5363 final long identityToken = clearCallingIdentity(); 5364 try { 5365 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5366 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 5367 } 5368 5369 if (intent.getComponent() != null && 5370 GrantCredentialsPermissionActivity.class.getName().equals( 5371 intent.getComponent().getClassName())) { 5372 createNoCredentialsPermissionNotification(account, intent, packageName, userId); 5373 } else { 5374 Context contextForUser = getContextForUser(new UserHandle(userId)); 5375 final NotificationId id = getSigninRequiredNotificationId(accounts, account); 5376 intent.addCategory(id.mTag); 5377 5378 final String notificationTitleFormat = 5379 contextForUser.getText(R.string.notification_title).toString(); 5380 Notification n = 5381 new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) 5382 .setWhen(0) 5383 .setSmallIcon(android.R.drawable.stat_sys_warning) 5384 .setColor(contextForUser.getColor( 5385 com.android.internal.R.color.system_notification_accent_color)) 5386 .setContentTitle(String.format(notificationTitleFormat, account.name)) 5387 .setContentText(message) 5388 .setContentIntent(PendingIntent.getActivityAsUser( 5389 mContext, 0, intent, 5390 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 5391 null, new UserHandle(userId))) 5392 .build(); 5393 installNotification(id, n, packageName, userId); 5394 } 5395 } finally { 5396 restoreCallingIdentity(identityToken); 5397 } 5398 } 5399 5400 private void installNotification(NotificationId id, final Notification notification, 5401 String packageName, int userId) { 5402 final long token = clearCallingIdentity(); 5403 try { 5404 INotificationManager notificationManager = mInjector.getNotificationManager(); 5405 try { 5406 // The calling uid must match either the package or op package, so use an op 5407 // package that matches the cleared calling identity. 5408 notificationManager.enqueueNotificationWithTag(packageName, "android", 5409 id.mTag, id.mId, notification, userId); 5410 } catch (RemoteException e) { 5411 /* ignore - local call */ 5412 } 5413 } finally { 5414 Binder.restoreCallingIdentity(token); 5415 } 5416 } 5417 5418 private void cancelNotification(NotificationId id, UserHandle user) { 5419 cancelNotification(id, mContext.getPackageName(), user); 5420 } 5421 5422 private void cancelNotification(NotificationId id, String packageName, UserHandle user) { 5423 final long identityToken = clearCallingIdentity(); 5424 try { 5425 INotificationManager service = mInjector.getNotificationManager(); 5426 service.cancelNotificationWithTag( 5427 packageName, "android", id.mTag, id.mId, user.getIdentifier()); 5428 } catch (RemoteException e) { 5429 /* ignore - local call */ 5430 } finally { 5431 restoreCallingIdentity(identityToken); 5432 } 5433 } 5434 5435 private boolean isPermittedForPackage(String packageName, int userId, String... permissions) { 5436 final long identity = Binder.clearCallingIdentity(); 5437 try { 5438 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); 5439 IPackageManager pm = ActivityThread.getPackageManager(); 5440 for (String perm : permissions) { 5441 if (pm.checkPermission(perm, packageName, userId) 5442 == PackageManager.PERMISSION_GRANTED) { 5443 // Checks runtime permission revocation. 5444 final int opCode = AppOpsManager.permissionToOpCode(perm); 5445 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.checkOpNoThrow( 5446 opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED) { 5447 return true; 5448 } 5449 } 5450 } 5451 } catch (NameNotFoundException | RemoteException e) { 5452 // Assume permission is not granted if an error accrued. 5453 } finally { 5454 Binder.restoreCallingIdentity(identity); 5455 } 5456 return false; 5457 } 5458 5459 /** 5460 * Checks that package has at least one of given permissions and makes note of app 5461 * performing the action. 5462 */ 5463 private boolean checkPermissionAndNote(String opPackageName, int callingUid, 5464 String... permissions) { 5465 for (String perm : permissions) { 5466 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 5467 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5468 Log.v(TAG, " caller uid " + callingUid + " has " + perm); 5469 } 5470 final int opCode = AppOpsManager.permissionToOpCode(perm); 5471 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( 5472 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { 5473 return true; 5474 } 5475 } 5476 } 5477 return false; 5478 } 5479 5480 private int handleIncomingUser(int userId) { 5481 try { 5482 return ActivityManager.getService().handleIncomingUser( 5483 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 5484 } catch (RemoteException re) { 5485 // Shouldn't happen, local. 5486 } 5487 return userId; 5488 } 5489 5490 private boolean isPrivileged(int callingUid) { 5491 String[] packages; 5492 final long identityToken = Binder.clearCallingIdentity(); 5493 try { 5494 packages = mPackageManager.getPackagesForUid(callingUid); 5495 if (packages == null) { 5496 Log.d(TAG, "No packages for callingUid " + callingUid); 5497 return false; 5498 } 5499 for (String name : packages) { 5500 try { 5501 PackageInfo packageInfo = 5502 mPackageManager.getPackageInfo(name, 0 /* flags */); 5503 if (packageInfo != null 5504 && (packageInfo.applicationInfo.privateFlags 5505 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 5506 return true; 5507 } 5508 } catch (PackageManager.NameNotFoundException e) { 5509 Log.d(TAG, "Package not found " + e.getMessage()); 5510 } 5511 } 5512 } finally { 5513 Binder.restoreCallingIdentity(identityToken); 5514 } 5515 return false; 5516 } 5517 5518 private boolean permissionIsGranted( 5519 Account account, String authTokenType, int callerUid, int userId) { 5520 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { 5521 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5522 Log.v(TAG, "Access to " + account + " granted calling uid is system"); 5523 } 5524 return true; 5525 } 5526 5527 if (isPrivileged(callerUid)) { 5528 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5529 Log.v(TAG, "Access to " + account + " granted calling uid " 5530 + callerUid + " privileged"); 5531 } 5532 return true; 5533 } 5534 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) { 5535 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5536 Log.v(TAG, "Access to " + account + " granted calling uid " 5537 + callerUid + " manages the account"); 5538 } 5539 return true; 5540 } 5541 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) { 5542 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5543 Log.v(TAG, "Access to " + account + " granted calling uid " 5544 + callerUid + " user granted access"); 5545 } 5546 return true; 5547 } 5548 5549 if (Log.isLoggable(TAG, Log.VERBOSE)) { 5550 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid); 5551 } 5552 5553 return false; 5554 } 5555 5556 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, 5557 String opPackageName) { 5558 if (accountType == null) { 5559 return false; 5560 } else { 5561 return getTypesVisibleToCaller(callingUid, userId, 5562 opPackageName).contains(accountType); 5563 } 5564 } 5565 5566 // Method checks visibility for applications targeing API level below {@link 5567 // android.os.Build.VERSION_CODES#O}, 5568 // returns true if the the app has GET_ACCOUNTS or GET_ACCOUNTS_PRIVILEGED permission. 5569 private boolean checkGetAccountsPermission(String packageName, int userId) { 5570 return isPermittedForPackage(packageName, userId, Manifest.permission.GET_ACCOUNTS, 5571 Manifest.permission.GET_ACCOUNTS_PRIVILEGED); 5572 } 5573 5574 private boolean checkReadContactsPermission(String packageName, int userId) { 5575 return isPermittedForPackage(packageName, userId, Manifest.permission.READ_CONTACTS); 5576 } 5577 5578 // Heuristic to check that account type may be associated with some contacts data and 5579 // therefore READ_CONTACTS permission grants the access to account by default. 5580 private boolean accountTypeManagesContacts(String accountType, int userId) { 5581 if (accountType == null) { 5582 return false; 5583 } 5584 final long identityToken = Binder.clearCallingIdentity(); 5585 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 5586 try { 5587 serviceInfos = mAuthenticatorCache.getAllServices(userId); 5588 } finally { 5589 Binder.restoreCallingIdentity(identityToken); 5590 } 5591 // Check contacts related permissions for authenticator. 5592 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo 5593 : serviceInfos) { 5594 if (accountType.equals(serviceInfo.type.type)) { 5595 return isPermittedForPackage(serviceInfo.type.packageName, userId, 5596 Manifest.permission.WRITE_CONTACTS); 5597 } 5598 } 5599 return false; 5600 } 5601 5602 /** 5603 * Method checks package uid and signature with Authenticator which manages accountType. 5604 * 5605 * @return SIGNATURE_CHECK_UID_MATCH for uid match, SIGNATURE_CHECK_MATCH for signature match, 5606 * SIGNATURE_CHECK_MISMATCH otherwise. 5607 */ 5608 private int checkPackageSignature(String accountType, int callingUid, int userId) { 5609 if (accountType == null) { 5610 return SIGNATURE_CHECK_MISMATCH; 5611 } 5612 5613 final long identityToken = Binder.clearCallingIdentity(); 5614 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 5615 try { 5616 serviceInfos = mAuthenticatorCache.getAllServices(userId); 5617 } finally { 5618 Binder.restoreCallingIdentity(identityToken); 5619 } 5620 // Check for signature match with Authenticator.LocalServices.getService(PackageManagerInternal.class); 5621 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 5622 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo 5623 : serviceInfos) { 5624 if (accountType.equals(serviceInfo.type.type)) { 5625 if (serviceInfo.uid == callingUid) { 5626 return SIGNATURE_CHECK_UID_MATCH; 5627 } 5628 if (pmi.hasSignatureCapability( 5629 serviceInfo.uid, callingUid, 5630 PackageParser.SigningDetails.CertCapabilities.AUTH)) { 5631 return SIGNATURE_CHECK_MATCH; 5632 } 5633 } 5634 } 5635 return SIGNATURE_CHECK_MISMATCH; 5636 } 5637 5638 // returns true for applications with the same signature as authenticator. 5639 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { 5640 if (accountType == null) { 5641 return false; 5642 } else { 5643 return getTypesManagedByCaller(callingUid, userId).contains(accountType); 5644 } 5645 } 5646 5647 private List<String> getTypesVisibleToCaller(int callingUid, int userId, 5648 String opPackageName) { 5649 return getTypesForCaller(callingUid, userId, true /* isOtherwisePermitted*/); 5650 } 5651 5652 private List<String> getTypesManagedByCaller(int callingUid, int userId) { 5653 return getTypesForCaller(callingUid, userId, false); 5654 } 5655 5656 private List<String> getTypesForCaller( 5657 int callingUid, int userId, boolean isOtherwisePermitted) { 5658 List<String> managedAccountTypes = new ArrayList<>(); 5659 final long identityToken = Binder.clearCallingIdentity(); 5660 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 5661 try { 5662 serviceInfos = mAuthenticatorCache.getAllServices(userId); 5663 } finally { 5664 Binder.restoreCallingIdentity(identityToken); 5665 } 5666 5667 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 5668 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 5669 serviceInfos) { 5670 if (isOtherwisePermitted || pmi.hasSignatureCapability( 5671 serviceInfo.uid, callingUid, 5672 PackageParser.SigningDetails.CertCapabilities.AUTH)) { 5673 managedAccountTypes.add(serviceInfo.type.type); 5674 } 5675 } 5676 return managedAccountTypes; 5677 } 5678 5679 private boolean isAccountPresentForCaller(String accountName, String accountType) { 5680 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) { 5681 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) { 5682 if (account.name.equals(accountName)) { 5683 return true; 5684 } 5685 } 5686 } 5687 return false; 5688 } 5689 5690 private static void checkManageUsersPermission(String message) { 5691 if (ActivityManager.checkComponentPermission( 5692 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true) 5693 != PackageManager.PERMISSION_GRANTED) { 5694 throw new SecurityException("You need MANAGE_USERS permission to: " + message); 5695 } 5696 } 5697 5698 private static void checkManageOrCreateUsersPermission(String message) { 5699 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS, 5700 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED && 5701 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS, 5702 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) { 5703 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: " 5704 + message); 5705 } 5706 } 5707 5708 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 5709 int callerUid) { 5710 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { 5711 return true; 5712 } 5713 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid)); 5714 synchronized (accounts.dbLock) { 5715 synchronized (accounts.cacheLock) { 5716 long grantsCount; 5717 if (authTokenType != null) { 5718 grantsCount = accounts.accountsDb 5719 .findMatchingGrantsCount(callerUid, authTokenType, account); 5720 } else { 5721 grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid, 5722 account); 5723 } 5724 final boolean permissionGranted = grantsCount > 0; 5725 5726 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 5727 // TODO: Skip this check when running automated tests. Replace this 5728 // with a more general solution. 5729 Log.d(TAG, "no credentials permission for usage of " 5730 + account.toSafeString() + ", " 5731 + authTokenType + " by uid " + callerUid 5732 + " but ignoring since device is in test harness."); 5733 return true; 5734 } 5735 return permissionGranted; 5736 } 5737 } 5738 } 5739 5740 private boolean isSystemUid(int callingUid) { 5741 String[] packages = null; 5742 final long ident = Binder.clearCallingIdentity(); 5743 try { 5744 packages = mPackageManager.getPackagesForUid(callingUid); 5745 if (packages != null) { 5746 for (String name : packages) { 5747 try { 5748 PackageInfo packageInfo = 5749 mPackageManager.getPackageInfo(name, 0 /* flags */); 5750 if (packageInfo != null 5751 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 5752 != 0) { 5753 return true; 5754 } 5755 } catch (NameNotFoundException e) { 5756 Log.w(TAG, String.format("Could not find package [%s]", name), e); 5757 } 5758 } 5759 } else { 5760 Log.w(TAG, "No known packages with uid " + callingUid); 5761 } 5762 } finally { 5763 Binder.restoreCallingIdentity(ident); 5764 } 5765 return false; 5766 } 5767 5768 /** Succeeds if any of the specified permissions are granted. */ 5769 private void checkReadAccountsPermitted( 5770 int callingUid, 5771 String accountType, 5772 int userId, 5773 String opPackageName) { 5774 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { 5775 String msg = String.format( 5776 "caller uid %s cannot access %s accounts", 5777 callingUid, 5778 accountType); 5779 Log.w(TAG, " " + msg); 5780 throw new SecurityException(msg); 5781 } 5782 } 5783 5784 private boolean canUserModifyAccounts(int userId, int callingUid) { 5785 // the managing app can always modify accounts 5786 if (isProfileOwner(callingUid)) { 5787 return true; 5788 } 5789 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 5790 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 5791 return false; 5792 } 5793 return true; 5794 } 5795 5796 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) { 5797 // the managing app can always modify accounts 5798 if (isProfileOwner(callingUid)) { 5799 return true; 5800 } 5801 DevicePolicyManager dpm = (DevicePolicyManager) mContext 5802 .getSystemService(Context.DEVICE_POLICY_SERVICE); 5803 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 5804 if (typesArray == null) { 5805 return true; 5806 } 5807 for (String forbiddenType : typesArray) { 5808 if (forbiddenType.equals(accountType)) { 5809 return false; 5810 } 5811 } 5812 return true; 5813 } 5814 5815 private boolean isProfileOwner(int uid) { 5816 final DevicePolicyManagerInternal dpmi = 5817 LocalServices.getService(DevicePolicyManagerInternal.class); 5818 //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. 5819 return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid)); 5820 } 5821 5822 @Override 5823 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 5824 throws RemoteException { 5825 final int callingUid = getCallingUid(); 5826 5827 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { 5828 throw new SecurityException(); 5829 } 5830 5831 if (value) { 5832 grantAppPermission(account, authTokenType, uid); 5833 } else { 5834 revokeAppPermission(account, authTokenType, uid); 5835 } 5836 } 5837 5838 /** 5839 * Allow callers with the given uid permission to get credentials for account/authTokenType. 5840 * <p> 5841 * Although this is public it can only be accessed via the AccountManagerService object 5842 * which is in the system. This means we don't need to protect it with permissions. 5843 * @hide 5844 */ 5845 void grantAppPermission(Account account, String authTokenType, int uid) { 5846 if (account == null || authTokenType == null) { 5847 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 5848 return; 5849 } 5850 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5851 synchronized (accounts.dbLock) { 5852 synchronized (accounts.cacheLock) { 5853 long accountId = accounts.accountsDb.findDeAccountId(account); 5854 if (accountId >= 0) { 5855 accounts.accountsDb.insertGrant(accountId, authTokenType, uid); 5856 } 5857 cancelNotification( 5858 getCredentialPermissionNotificationId(account, authTokenType, uid), 5859 UserHandle.of(accounts.userId)); 5860 5861 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); 5862 } 5863 } 5864 5865 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 5866 for (AccountManagerInternal.OnAppPermissionChangeListener listener 5867 : mAppPermissionChangeListeners) { 5868 mHandler.post(() -> listener.onAppPermissionChanged(account, uid)); 5869 } 5870 } 5871 5872 /** 5873 * Don't allow callers with the given uid permission to get credentials for 5874 * account/authTokenType. 5875 * <p> 5876 * Although this is public it can only be accessed via the AccountManagerService object 5877 * which is in the system. This means we don't need to protect it with permissions. 5878 * @hide 5879 */ 5880 private void revokeAppPermission(Account account, String authTokenType, int uid) { 5881 if (account == null || authTokenType == null) { 5882 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 5883 return; 5884 } 5885 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 5886 synchronized (accounts.dbLock) { 5887 synchronized (accounts.cacheLock) { 5888 accounts.accountsDb.beginTransaction(); 5889 try { 5890 long accountId = accounts.accountsDb.findDeAccountId(account); 5891 if (accountId >= 0) { 5892 accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid( 5893 accountId, authTokenType, uid); 5894 accounts.accountsDb.setTransactionSuccessful(); 5895 } 5896 } finally { 5897 accounts.accountsDb.endTransaction(); 5898 } 5899 5900 cancelNotification( 5901 getCredentialPermissionNotificationId(account, authTokenType, uid), 5902 UserHandle.of(accounts.userId)); 5903 } 5904 } 5905 5906 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 5907 for (AccountManagerInternal.OnAppPermissionChangeListener listener 5908 : mAppPermissionChangeListeners) { 5909 mHandler.post(() -> listener.onAppPermissionChanged(account, uid)); 5910 } 5911 } 5912 5913 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 5914 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 5915 if (oldAccountsForType != null) { 5916 ArrayList<Account> newAccountsList = new ArrayList<>(); 5917 for (Account curAccount : oldAccountsForType) { 5918 if (!curAccount.equals(account)) { 5919 newAccountsList.add(curAccount); 5920 } 5921 } 5922 if (newAccountsList.isEmpty()) { 5923 accounts.accountCache.remove(account.type); 5924 } else { 5925 Account[] newAccountsForType = new Account[newAccountsList.size()]; 5926 newAccountsForType = newAccountsList.toArray(newAccountsForType); 5927 accounts.accountCache.put(account.type, newAccountsForType); 5928 } 5929 } 5930 accounts.userDataCache.remove(account); 5931 accounts.authTokenCache.remove(account); 5932 accounts.previousNameCache.remove(account); 5933 accounts.visibilityCache.remove(account); 5934 5935 AccountManager.invalidateLocalAccountsDataCaches(); 5936 } 5937 5938 /** 5939 * This assumes that the caller has already checked that the account is not already present. 5940 * IMPORTANT: The account being inserted will begin to be tracked for access in remote 5941 * processes and if you will return this account to apps you should return the result. 5942 * @return The inserted account which is a new instance that is being tracked. 5943 */ 5944 private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 5945 Account[] accountsForType = accounts.accountCache.get(account.type); 5946 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 5947 Account[] newAccountsForType = new Account[oldLength + 1]; 5948 if (accountsForType != null) { 5949 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 5950 } 5951 String token = account.getAccessId() != null ? account.getAccessId() 5952 : UUID.randomUUID().toString(); 5953 newAccountsForType[oldLength] = new Account(account, token); 5954 accounts.accountCache.put(account.type, newAccountsForType); 5955 AccountManager.invalidateLocalAccountsDataCaches(); 5956 return newAccountsForType[oldLength]; 5957 } 5958 5959 @NonNull 5960 private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid, 5961 @Nullable String callingPackage, boolean includeManagedNotVisible) { 5962 String visibilityFilterPackage = callingPackage; 5963 if (visibilityFilterPackage == null) { 5964 visibilityFilterPackage = getPackageNameForUid(callingUid); 5965 } 5966 Map<Account, Integer> firstPass = new LinkedHashMap<>(); 5967 for (Account account : unfiltered) { 5968 int visibility = resolveAccountVisibility(account, visibilityFilterPackage, accounts); 5969 if ((visibility == AccountManager.VISIBILITY_VISIBLE 5970 || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE) 5971 || (includeManagedNotVisible 5972 && (visibility 5973 == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE))) { 5974 firstPass.put(account, visibility); 5975 } 5976 } 5977 Map<Account, Integer> secondPass = 5978 filterSharedAccounts(accounts, firstPass, callingUid, callingPackage); 5979 5980 Account[] filtered = new Account[secondPass.size()]; 5981 filtered = secondPass.keySet().toArray(filtered); 5982 return filtered; 5983 } 5984 5985 @NonNull 5986 private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts, 5987 @NonNull Map<Account, Integer> unfiltered, int callingUid, 5988 @Nullable String callingPackage) { 5989 // first part is to filter shared accounts. 5990 // unfiltered type check is not necessary. 5991 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 5992 || callingUid == Process.SYSTEM_UID) { 5993 return unfiltered; 5994 } 5995 UserInfo user = getUserManager().getUserInfo(userAccounts.userId); 5996 if (user != null && user.isRestricted()) { 5997 String[] packages = mPackageManager.getPackagesForUid(callingUid); 5998 if (packages == null) { 5999 packages = new String[] {}; 6000 } 6001 // If any of the packages is a visible listed package, return the full set, 6002 // otherwise return non-shared accounts only. 6003 // This might be a temporary way to specify a visible list 6004 String visibleList = mContext.getResources().getString( 6005 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 6006 for (String packageName : packages) { 6007 if (visibleList.contains(";" + packageName + ";")) { 6008 return unfiltered; 6009 } 6010 } 6011 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 6012 if (ArrayUtils.isEmpty(sharedAccounts)) { 6013 return unfiltered; 6014 } 6015 String requiredAccountType = ""; 6016 try { 6017 // If there's an explicit callingPackage specified, check if that package 6018 // opted in to see restricted accounts. 6019 if (callingPackage != null) { 6020 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 6021 if (pi != null && pi.restrictedAccountType != null) { 6022 requiredAccountType = pi.restrictedAccountType; 6023 } 6024 } else { 6025 // Otherwise check if the callingUid has a package that has opted in 6026 for (String packageName : packages) { 6027 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 6028 if (pi != null && pi.restrictedAccountType != null) { 6029 requiredAccountType = pi.restrictedAccountType; 6030 break; 6031 } 6032 } 6033 } 6034 } catch (NameNotFoundException e) { 6035 Log.d(TAG, "Package not found " + e.getMessage()); 6036 } 6037 Map<Account, Integer> filtered = new LinkedHashMap<>(); 6038 for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) { 6039 Account account = entry.getKey(); 6040 if (account.type.equals(requiredAccountType)) { 6041 filtered.put(account, entry.getValue()); 6042 } else { 6043 boolean found = false; 6044 for (Account shared : sharedAccounts) { 6045 if (shared.equals(account)) { 6046 found = true; 6047 break; 6048 } 6049 } 6050 if (!found) { 6051 filtered.put(account, entry.getValue()); 6052 } 6053 } 6054 } 6055 return filtered; 6056 } else { 6057 return unfiltered; 6058 } 6059 } 6060 6061 /* 6062 * packageName can be null. If not null, it should be used to filter out restricted accounts 6063 * that the package is not allowed to access. 6064 * 6065 * <p>The method shouldn't be called with UserAccounts#cacheLock held, otherwise it will cause a 6066 * deadlock 6067 */ 6068 @NonNull 6069 protected Account[] getAccountsFromCache(UserAccounts userAccounts, String accountType, 6070 int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) { 6071 Preconditions.checkState(!Thread.holdsLock(userAccounts.cacheLock), 6072 "Method should not be called with cacheLock"); 6073 if (accountType != null) { 6074 Account[] accounts; 6075 synchronized (userAccounts.cacheLock) { 6076 accounts = userAccounts.accountCache.get(accountType); 6077 } 6078 if (accounts == null) { 6079 return EMPTY_ACCOUNT_ARRAY; 6080 } else { 6081 return filterAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 6082 callingUid, callingPackage, includeManagedNotVisible); 6083 } 6084 } else { 6085 int totalLength = 0; 6086 Account[] accountsArray; 6087 synchronized (userAccounts.cacheLock) { 6088 for (Account[] accounts : userAccounts.accountCache.values()) { 6089 totalLength += accounts.length; 6090 } 6091 if (totalLength == 0) { 6092 return EMPTY_ACCOUNT_ARRAY; 6093 } 6094 accountsArray = new Account[totalLength]; 6095 totalLength = 0; 6096 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 6097 System.arraycopy(accountsOfType, 0, accountsArray, totalLength, 6098 accountsOfType.length); 6099 totalLength += accountsOfType.length; 6100 } 6101 } 6102 return filterAccounts(userAccounts, accountsArray, callingUid, callingPackage, 6103 includeManagedNotVisible); 6104 } 6105 } 6106 6107 /** protected by the {@code dbLock}, {@code cacheLock} */ 6108 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, 6109 Account account, String key, String value) { 6110 Map<String, String> userDataForAccount = accounts.userDataCache.get(account); 6111 if (userDataForAccount == null) { 6112 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account); 6113 accounts.userDataCache.put(account, userDataForAccount); 6114 } 6115 if (value == null) { 6116 userDataForAccount.remove(key); 6117 } else { 6118 userDataForAccount.put(key, value); 6119 } 6120 } 6121 6122 protected String readCachedTokenInternal( 6123 UserAccounts accounts, 6124 Account account, 6125 String tokenType, 6126 String callingPackage, 6127 byte[] pkgSigDigest) { 6128 synchronized (accounts.cacheLock) { 6129 return accounts.accountTokenCaches.get( 6130 account, tokenType, callingPackage, pkgSigDigest); 6131 } 6132 } 6133 6134 /** protected by the {@code dbLock}, {@code cacheLock} */ 6135 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, 6136 Account account, String key, String value) { 6137 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 6138 if (authTokensForAccount == null) { 6139 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account); 6140 accounts.authTokenCache.put(account, authTokensForAccount); 6141 } 6142 if (value == null) { 6143 authTokensForAccount.remove(key); 6144 } else { 6145 authTokensForAccount.put(key, value); 6146 } 6147 } 6148 6149 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 6150 String authTokenType) { 6151 // Fast path - check if account is already cached 6152 synchronized (accounts.cacheLock) { 6153 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 6154 if (authTokensForAccount != null) { 6155 return authTokensForAccount.get(authTokenType); 6156 } 6157 } 6158 // If not cached yet - do slow path and sync with db if necessary 6159 synchronized (accounts.dbLock) { 6160 synchronized (accounts.cacheLock) { 6161 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 6162 if (authTokensForAccount == null) { 6163 // need to populate the cache for this account 6164 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account); 6165 accounts.authTokenCache.put(account, authTokensForAccount); 6166 } 6167 return authTokensForAccount.get(authTokenType); 6168 } 6169 } 6170 } 6171 6172 private String readUserDataInternal(UserAccounts accounts, Account account, String key) { 6173 Map<String, String> userDataForAccount; 6174 // Fast path - check if data is already cached 6175 synchronized (accounts.cacheLock) { 6176 userDataForAccount = accounts.userDataCache.get(account); 6177 } 6178 // If not cached yet - do slow path and sync with db if necessary 6179 if (userDataForAccount == null) { 6180 synchronized (accounts.dbLock) { 6181 synchronized (accounts.cacheLock) { 6182 userDataForAccount = accounts.userDataCache.get(account); 6183 if (userDataForAccount == null) { 6184 // need to populate the cache for this account 6185 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account); 6186 accounts.userDataCache.put(account, userDataForAccount); 6187 } 6188 } 6189 } 6190 } 6191 return userDataForAccount.get(key); 6192 } 6193 6194 private Context getContextForUser(UserHandle user) { 6195 try { 6196 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 6197 } catch (NameNotFoundException e) { 6198 // Default to mContext, not finding the package system is running as is unlikely. 6199 return mContext; 6200 } 6201 } 6202 6203 private void sendResponse(IAccountManagerResponse response, Bundle result) { 6204 try { 6205 response.onResult(result); 6206 } catch (RemoteException e) { 6207 // if the caller is dead then there is no one to care about remote 6208 // exceptions 6209 if (Log.isLoggable(TAG, Log.VERBOSE)) { 6210 Log.v(TAG, "failure while notifying response", e); 6211 } 6212 } 6213 } 6214 6215 private void sendErrorResponse(IAccountManagerResponse response, int errorCode, 6216 String errorMessage) { 6217 try { 6218 response.onError(errorCode, errorMessage); 6219 } catch (RemoteException e) { 6220 // if the caller is dead then there is no one to care about remote 6221 // exceptions 6222 if (Log.isLoggable(TAG, Log.VERBOSE)) { 6223 Log.v(TAG, "failure while notifying response", e); 6224 } 6225 } 6226 } 6227 6228 private final class AccountManagerInternalImpl extends AccountManagerInternal { 6229 private final Object mLock = new Object(); 6230 6231 @GuardedBy("mLock") 6232 private AccountManagerBackupHelper mBackupHelper; 6233 6234 @Override 6235 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, 6236 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { 6237 if (account == null) { 6238 Slog.w(TAG, "account cannot be null"); 6239 return; 6240 } 6241 if (packageName == null) { 6242 Slog.w(TAG, "packageName cannot be null"); 6243 return; 6244 } 6245 if (userId < UserHandle.USER_SYSTEM) { 6246 Slog.w(TAG, "user id must be concrete"); 6247 return; 6248 } 6249 if (callback == null) { 6250 Slog.w(TAG, "callback cannot be null"); 6251 return; 6252 } 6253 6254 int visibility = 6255 resolveAccountVisibility(account, packageName, getUserAccounts(userId)); 6256 if (visibility == AccountManager.VISIBILITY_NOT_VISIBLE) { 6257 Slog.w(TAG, "requestAccountAccess: account is hidden"); 6258 return; 6259 } 6260 6261 if (AccountManagerService.this.hasAccountAccess(account, packageName, 6262 new UserHandle(userId))) { 6263 Bundle result = new Bundle(); 6264 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 6265 callback.sendResult(result); 6266 return; 6267 } 6268 6269 final int uid; 6270 try { 6271 final long identityToken = clearCallingIdentity(); 6272 try { 6273 uid = mPackageManager.getPackageUidAsUser(packageName, userId); 6274 } finally { 6275 restoreCallingIdentity(identityToken); 6276 } 6277 } catch (NameNotFoundException e) { 6278 Slog.e(TAG, "Unknown package " + packageName); 6279 return; 6280 } 6281 6282 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); 6283 final UserAccounts userAccounts; 6284 synchronized (mUsers) { 6285 userAccounts = mUsers.get(userId); 6286 } 6287 SystemNotificationChannels.createAccountChannelForPackage(packageName, uid, mContext); 6288 doNotification(userAccounts, account, null, intent, packageName, userId); 6289 } 6290 6291 @Override 6292 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) { 6293 // Listeners are a final CopyOnWriteArrayList, hence no lock needed. 6294 mAppPermissionChangeListeners.add(listener); 6295 } 6296 6297 @Override 6298 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) { 6299 return AccountManagerService.this.hasAccountAccess(account, null, uid); 6300 } 6301 6302 @Override 6303 public byte[] backupAccountAccessPermissions(int userId) { 6304 synchronized (mLock) { 6305 if (mBackupHelper == null) { 6306 mBackupHelper = new AccountManagerBackupHelper( 6307 AccountManagerService.this, this); 6308 } 6309 return mBackupHelper.backupAccountAccessPermissions(userId); 6310 } 6311 } 6312 6313 @Override 6314 public void restoreAccountAccessPermissions(byte[] data, int userId) { 6315 synchronized (mLock) { 6316 if (mBackupHelper == null) { 6317 mBackupHelper = new AccountManagerBackupHelper( 6318 AccountManagerService.this, this); 6319 } 6320 mBackupHelper.restoreAccountAccessPermissions(data, userId); 6321 } 6322 } 6323 } 6324 6325 @VisibleForTesting 6326 static class Injector { 6327 private final Context mContext; 6328 6329 public Injector(Context context) { 6330 mContext = context; 6331 } 6332 6333 Looper getMessageHandlerLooper() { 6334 ServiceThread serviceThread = new ServiceThread(TAG, 6335 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); 6336 serviceThread.start(); 6337 return serviceThread.getLooper(); 6338 } 6339 6340 Context getContext() { 6341 return mContext; 6342 } 6343 6344 void addLocalService(AccountManagerInternal service) { 6345 LocalServices.addService(AccountManagerInternal.class, service); 6346 } 6347 6348 String getDeDatabaseName(int userId) { 6349 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId), 6350 AccountsDb.DE_DATABASE_NAME); 6351 return databaseFile.getPath(); 6352 } 6353 6354 String getCeDatabaseName(int userId) { 6355 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId), 6356 AccountsDb.CE_DATABASE_NAME); 6357 return databaseFile.getPath(); 6358 } 6359 6360 String getPreNDatabaseName(int userId) { 6361 File systemDir = Environment.getDataSystemDirectory(); 6362 File databaseFile = new File(Environment.getUserSystemDirectory(userId), 6363 PRE_N_DATABASE_NAME); 6364 if (userId == 0) { 6365 // Migrate old file, if it exists, to the new location. 6366 // Make sure the new file doesn't already exist. A placeholder file could have been 6367 // accidentally created in the old location, 6368 // causing the new one to become corrupted as well. 6369 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME); 6370 if (oldFile.exists() && !databaseFile.exists()) { 6371 // Check for use directory; create if it doesn't exist, else renameTo will fail 6372 File userDir = Environment.getUserSystemDirectory(userId); 6373 if (!userDir.exists()) { 6374 if (!userDir.mkdirs()) { 6375 throw new IllegalStateException( 6376 "User dir cannot be created: " + userDir); 6377 } 6378 } 6379 if (!oldFile.renameTo(databaseFile)) { 6380 throw new IllegalStateException( 6381 "User dir cannot be migrated: " + databaseFile); 6382 } 6383 } 6384 } 6385 return databaseFile.getPath(); 6386 } 6387 6388 IAccountAuthenticatorCache getAccountAuthenticatorCache() { 6389 return new AccountAuthenticatorCache(mContext); 6390 } 6391 6392 INotificationManager getNotificationManager() { 6393 return NotificationManager.getService(); 6394 } 6395 } 6396 6397 private static class NotificationId { 6398 final String mTag; 6399 private final int mId; 6400 6401 NotificationId(String tag, int type) { 6402 mTag = tag; 6403 mId = type; 6404 } 6405 } 6406 } 6407