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