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