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