AccountManagerService.java revision 02bffab9dc6cc50dd0efdb4bff4feaa9672de5ea
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.app.ActivityManager; 33import android.app.ActivityManagerNative; 34import android.app.AppGlobals; 35import android.app.AppOpsManager; 36import android.app.Notification; 37import android.app.NotificationManager; 38import android.app.PendingIntent; 39import android.app.admin.DevicePolicyManager; 40import android.content.BroadcastReceiver; 41import android.content.ComponentName; 42import android.content.ContentValues; 43import android.content.Context; 44import android.content.Intent; 45import android.content.IntentFilter; 46import android.content.ServiceConnection; 47import android.content.pm.ApplicationInfo; 48import android.content.pm.PackageInfo; 49import android.content.pm.PackageManager; 50import android.content.pm.PackageManager.NameNotFoundException; 51import android.content.pm.RegisteredServicesCache; 52import android.content.pm.RegisteredServicesCacheListener; 53import android.content.pm.ResolveInfo; 54import android.content.pm.Signature; 55import android.content.pm.UserInfo; 56import android.database.Cursor; 57import android.database.DatabaseUtils; 58import android.database.sqlite.SQLiteDatabase; 59import android.database.sqlite.SQLiteOpenHelper; 60import android.database.sqlite.SQLiteStatement; 61import android.os.Binder; 62import android.os.Bundle; 63import android.os.Environment; 64import android.os.Handler; 65import android.os.IBinder; 66import android.os.Looper; 67import android.os.Message; 68import android.os.Parcel; 69import android.os.Process; 70import android.os.RemoteException; 71import android.os.SystemClock; 72import android.os.UserHandle; 73import android.os.UserManager; 74import android.text.TextUtils; 75import android.util.Log; 76import android.util.Pair; 77import android.util.Slog; 78import android.util.SparseArray; 79 80import com.android.internal.R; 81import com.android.internal.util.ArrayUtils; 82import com.android.internal.util.IndentingPrintWriter; 83import com.android.server.FgThread; 84import com.google.android.collect.Lists; 85import com.google.android.collect.Sets; 86 87import java.io.File; 88import java.io.FileDescriptor; 89import java.io.PrintWriter; 90import java.security.MessageDigest; 91import java.security.NoSuchAlgorithmException; 92import java.text.SimpleDateFormat; 93import java.util.ArrayList; 94import java.util.Arrays; 95import java.util.Collection; 96import java.util.Date; 97import java.util.HashMap; 98import java.util.HashSet; 99import java.util.LinkedHashMap; 100import java.util.List; 101import java.util.Map; 102import java.util.concurrent.atomic.AtomicInteger; 103import java.util.concurrent.atomic.AtomicReference; 104 105/** 106 * A system service that provides account, password, and authtoken management for all 107 * accounts on the device. Some of these calls are implemented with the help of the corresponding 108 * {@link IAccountAuthenticator} services. This service is not accessed by users directly, 109 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: 110 * AccountManager accountManager = AccountManager.get(context); 111 * @hide 112 */ 113public class AccountManagerService 114 extends IAccountManager.Stub 115 implements RegisteredServicesCacheListener<AuthenticatorDescription> { 116 private static final String TAG = "AccountManagerService"; 117 118 private static final String DATABASE_NAME = "accounts.db"; 119 private static final int DATABASE_VERSION = 8; 120 121 private static final int MAX_DEBUG_DB_SIZE = 64; 122 123 private final Context mContext; 124 125 private final PackageManager mPackageManager; 126 private final AppOpsManager mAppOpsManager; 127 private UserManager mUserManager; 128 129 private final MessageHandler mMessageHandler; 130 131 // Messages that can be sent on mHandler 132 private static final int MESSAGE_TIMED_OUT = 3; 133 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; 134 135 private final IAccountAuthenticatorCache mAuthenticatorCache; 136 137 private static final String TABLE_ACCOUNTS = "accounts"; 138 private static final String ACCOUNTS_ID = "_id"; 139 private static final String ACCOUNTS_NAME = "name"; 140 private static final String ACCOUNTS_TYPE = "type"; 141 private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; 142 private static final String ACCOUNTS_PASSWORD = "password"; 143 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name"; 144 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS = 145 "last_password_entry_time_millis_epoch"; 146 147 private static final String TABLE_AUTHTOKENS = "authtokens"; 148 private static final String AUTHTOKENS_ID = "_id"; 149 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; 150 private static final String AUTHTOKENS_TYPE = "type"; 151 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; 152 153 private static final String TABLE_GRANTS = "grants"; 154 private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; 155 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; 156 private static final String GRANTS_GRANTEE_UID = "uid"; 157 158 private static final String TABLE_EXTRAS = "extras"; 159 private static final String EXTRAS_ID = "_id"; 160 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; 161 private static final String EXTRAS_KEY = "key"; 162 private static final String EXTRAS_VALUE = "value"; 163 164 private static final String TABLE_META = "meta"; 165 private static final String META_KEY = "key"; 166 private static final String META_VALUE = "value"; 167 168 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts"; 169 170 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = 171 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; 172 private static final Intent ACCOUNTS_CHANGED_INTENT; 173 static { 174 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); 175 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 176 } 177 178 private static final String COUNT_OF_MATCHING_GRANTS = "" 179 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 180 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 181 + " AND " + GRANTS_GRANTEE_UID + "=?" 182 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" 183 + " AND " + ACCOUNTS_NAME + "=?" 184 + " AND " + ACCOUNTS_TYPE + "=?"; 185 186 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = 187 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 188 189 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, 190 AUTHTOKENS_AUTHTOKEN}; 191 192 private static final String SELECTION_USERDATA_BY_ACCOUNT = 193 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 194 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; 195 196 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); 197 private final AtomicInteger mNotificationIds = new AtomicInteger(1); 198 199 static class UserAccounts { 200 private final int userId; 201 private final DatabaseHelper openHelper; 202 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> 203 credentialsPermissionNotificationIds = 204 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); 205 private final HashMap<Account, Integer> signinRequiredNotificationIds = 206 new HashMap<Account, Integer>(); 207 private final Object cacheLock = new Object(); 208 /** protected by the {@link #cacheLock} */ 209 private final HashMap<String, Account[]> accountCache = 210 new LinkedHashMap<String, Account[]>(); 211 /** protected by the {@link #cacheLock} */ 212 private final HashMap<Account, HashMap<String, String>> userDataCache = 213 new HashMap<Account, HashMap<String, String>>(); 214 /** protected by the {@link #cacheLock} */ 215 private final HashMap<Account, HashMap<String, String>> authTokenCache = 216 new HashMap<Account, HashMap<String, String>>(); 217 218 /** protected by the {@link #cacheLock} */ 219 private final TokenCache accountTokenCaches = new TokenCache(); 220 221 /** 222 * protected by the {@link #cacheLock} 223 * 224 * Caches the previous names associated with an account. Previous names 225 * should be cached because we expect that when an Account is renamed, 226 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and 227 * want to know if the accounts they care about have been renamed. 228 * 229 * The previous names are wrapped in an {@link AtomicReference} so that 230 * we can distinguish between those accounts with no previous names and 231 * those whose previous names haven't been cached (yet). 232 */ 233 private final HashMap<Account, AtomicReference<String>> previousNameCache = 234 new HashMap<Account, AtomicReference<String>>(); 235 236 private int debugDbInsertionPoint = -1; 237 private SQLiteStatement statementForLogging; 238 239 UserAccounts(Context context, int userId) { 240 this.userId = userId; 241 synchronized (cacheLock) { 242 openHelper = new DatabaseHelper(context, userId); 243 } 244 } 245 } 246 247 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>(); 248 249 private static AtomicReference<AccountManagerService> sThis = 250 new AtomicReference<AccountManagerService>(); 251 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; 252 253 /** 254 * This should only be called by system code. One should only call this after the service 255 * has started. 256 * @return a reference to the AccountManagerService instance 257 * @hide 258 */ 259 public static AccountManagerService getSingleton() { 260 return sThis.get(); 261 } 262 263 public AccountManagerService(Context context) { 264 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); 265 } 266 267 public AccountManagerService(Context context, PackageManager packageManager, 268 IAccountAuthenticatorCache authenticatorCache) { 269 mContext = context; 270 mPackageManager = packageManager; 271 mAppOpsManager = mContext.getSystemService(AppOpsManager.class); 272 273 mMessageHandler = new MessageHandler(FgThread.get().getLooper()); 274 275 mAuthenticatorCache = authenticatorCache; 276 mAuthenticatorCache.setListener(this, null /* Handler */); 277 278 sThis.set(this); 279 280 IntentFilter intentFilter = new IntentFilter(); 281 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 282 intentFilter.addDataScheme("package"); 283 mContext.registerReceiver(new BroadcastReceiver() { 284 @Override 285 public void onReceive(Context context1, Intent intent) { 286 // Don't delete accounts when updating a authenticator's 287 // package. 288 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 289 /* Purging data requires file io, don't block the main thread. This is probably 290 * less than ideal because we are introducing a race condition where old grants 291 * could be exercised until they are purged. But that race condition existed 292 * anyway with the broadcast receiver. 293 * 294 * Ideally, we would completely clear the cache, purge data from the database, 295 * and then rebuild the cache. All under the cache lock. But that change is too 296 * large at this point. 297 */ 298 Runnable r = new Runnable() { 299 @Override 300 public void run() { 301 purgeOldGrantsAll(); 302 } 303 }; 304 new Thread(r).start(); 305 } 306 } 307 }, intentFilter); 308 309 IntentFilter userFilter = new IntentFilter(); 310 userFilter.addAction(Intent.ACTION_USER_REMOVED); 311 userFilter.addAction(Intent.ACTION_USER_STARTED); 312 mContext.registerReceiverAsUser(new BroadcastReceiver() { 313 @Override 314 public void onReceive(Context context, Intent intent) { 315 String action = intent.getAction(); 316 if (Intent.ACTION_USER_REMOVED.equals(action)) { 317 onUserRemoved(intent); 318 } else if (Intent.ACTION_USER_STARTED.equals(action)) { 319 onUserStarted(intent); 320 } 321 } 322 }, UserHandle.ALL, userFilter, null, null); 323 } 324 325 @Override 326 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 327 throws RemoteException { 328 try { 329 return super.onTransact(code, data, reply, flags); 330 } catch (RuntimeException e) { 331 // The account manager only throws security exceptions, so let's 332 // log all others. 333 if (!(e instanceof SecurityException)) { 334 Slog.wtf(TAG, "Account Manager Crash", e); 335 } 336 throw e; 337 } 338 } 339 340 public void systemReady() { 341 } 342 343 private UserManager getUserManager() { 344 if (mUserManager == null) { 345 mUserManager = UserManager.get(mContext); 346 } 347 return mUserManager; 348 } 349 350 /** 351 * Validate internal set of accounts against installed authenticators for 352 * given user. Clears cached authenticators before validating. 353 */ 354 public void validateAccounts(int userId) { 355 final UserAccounts accounts = getUserAccounts(userId); 356 357 // Invalidate user-specific cache to make sure we catch any 358 // removed authenticators. 359 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 360 } 361 362 /** 363 * Validate internal set of accounts against installed authenticators for 364 * given user. Clear cached authenticators before validating when requested. 365 */ 366 private void validateAccountsInternal( 367 UserAccounts accounts, boolean invalidateAuthenticatorCache) { 368 if (invalidateAuthenticatorCache) { 369 mAuthenticatorCache.invalidateCache(accounts.userId); 370 } 371 372 final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); 373 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : 374 mAuthenticatorCache.getAllServices(accounts.userId)) { 375 knownAuth.add(service.type); 376 } 377 378 synchronized (accounts.cacheLock) { 379 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 380 boolean accountDeleted = false; 381 Cursor cursor = db.query(TABLE_ACCOUNTS, 382 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, 383 null, null, null, null, ACCOUNTS_ID); 384 try { 385 accounts.accountCache.clear(); 386 final HashMap<String, ArrayList<String>> accountNamesByType = 387 new LinkedHashMap<String, ArrayList<String>>(); 388 while (cursor.moveToNext()) { 389 final long accountId = cursor.getLong(0); 390 final String accountType = cursor.getString(1); 391 final String accountName = cursor.getString(2); 392 393 if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { 394 Slog.w(TAG, "deleting account " + accountName + " because type " 395 + accountType + " no longer has a registered authenticator"); 396 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); 397 accountDeleted = true; 398 399 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS, 400 accountId, accounts); 401 402 final Account account = new Account(accountName, accountType); 403 accounts.userDataCache.remove(account); 404 accounts.authTokenCache.remove(account); 405 accounts.accountTokenCaches.remove(account); 406 } else { 407 ArrayList<String> accountNames = accountNamesByType.get(accountType); 408 if (accountNames == null) { 409 accountNames = new ArrayList<String>(); 410 accountNamesByType.put(accountType, accountNames); 411 } 412 accountNames.add(accountName); 413 } 414 } 415 for (Map.Entry<String, ArrayList<String>> cur 416 : accountNamesByType.entrySet()) { 417 final String accountType = cur.getKey(); 418 final ArrayList<String> accountNames = cur.getValue(); 419 final Account[] accountsForType = new Account[accountNames.size()]; 420 int i = 0; 421 for (String accountName : accountNames) { 422 accountsForType[i] = new Account(accountName, accountType); 423 ++i; 424 } 425 accounts.accountCache.put(accountType, accountsForType); 426 } 427 } finally { 428 cursor.close(); 429 if (accountDeleted) { 430 sendAccountsChangedBroadcast(accounts.userId); 431 } 432 } 433 } 434 } 435 436 private UserAccounts getUserAccountsForCaller() { 437 return getUserAccounts(UserHandle.getCallingUserId()); 438 } 439 440 protected UserAccounts getUserAccounts(int userId) { 441 synchronized (mUsers) { 442 UserAccounts accounts = mUsers.get(userId); 443 if (accounts == null) { 444 accounts = new UserAccounts(mContext, userId); 445 initializeDebugDbSizeAndCompileSqlStatementForLogging( 446 accounts.openHelper.getWritableDatabase(), accounts); 447 mUsers.append(userId, accounts); 448 purgeOldGrants(accounts); 449 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 450 } 451 return accounts; 452 } 453 } 454 455 private void purgeOldGrantsAll() { 456 synchronized (mUsers) { 457 for (int i = 0; i < mUsers.size(); i++) { 458 purgeOldGrants(mUsers.valueAt(i)); 459 } 460 } 461 } 462 463 private void purgeOldGrants(UserAccounts accounts) { 464 synchronized (accounts.cacheLock) { 465 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 466 final Cursor cursor = db.query(TABLE_GRANTS, 467 new String[]{GRANTS_GRANTEE_UID}, 468 null, null, GRANTS_GRANTEE_UID, null, null); 469 try { 470 while (cursor.moveToNext()) { 471 final int uid = cursor.getInt(0); 472 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; 473 if (packageExists) { 474 continue; 475 } 476 Log.d(TAG, "deleting grants for UID " + uid 477 + " because its package is no longer installed"); 478 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", 479 new String[]{Integer.toString(uid)}); 480 } 481 } finally { 482 cursor.close(); 483 } 484 } 485 } 486 487 private void onUserRemoved(Intent intent) { 488 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 489 if (userId < 1) return; 490 491 UserAccounts accounts; 492 synchronized (mUsers) { 493 accounts = mUsers.get(userId); 494 mUsers.remove(userId); 495 } 496 if (accounts == null) { 497 File dbFile = new File(getDatabaseName(userId)); 498 dbFile.delete(); 499 return; 500 } 501 502 synchronized (accounts.cacheLock) { 503 accounts.openHelper.close(); 504 File dbFile = new File(getDatabaseName(userId)); 505 dbFile.delete(); 506 } 507 } 508 509 private void onUserStarted(Intent intent) { 510 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 511 if (userId < 1) return; 512 513 // Check if there's a shared account that needs to be created as an account 514 Account[] sharedAccounts = getSharedAccountsAsUser(userId); 515 if (sharedAccounts == null || sharedAccounts.length == 0) return; 516 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName()); 517 for (Account sa : sharedAccounts) { 518 if (ArrayUtils.contains(accounts, sa)) continue; 519 // Account doesn't exist. Copy it now. 520 copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId); 521 } 522 } 523 524 @Override 525 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { 526 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); 527 } 528 529 @Override 530 public String getPassword(Account account) { 531 int callingUid = Binder.getCallingUid(); 532 if (Log.isLoggable(TAG, Log.VERBOSE)) { 533 Log.v(TAG, "getPassword: " + account 534 + ", caller's uid " + Binder.getCallingUid() 535 + ", pid " + Binder.getCallingPid()); 536 } 537 if (account == null) throw new IllegalArgumentException("account is null"); 538 int userId = UserHandle.getCallingUserId(); 539 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 540 String msg = String.format( 541 "uid %s cannot get secrets for accounts of type: %s", 542 callingUid, 543 account.type); 544 throw new SecurityException(msg); 545 } 546 long identityToken = clearCallingIdentity(); 547 try { 548 UserAccounts accounts = getUserAccounts(userId); 549 return readPasswordInternal(accounts, account); 550 } finally { 551 restoreCallingIdentity(identityToken); 552 } 553 } 554 555 private String readPasswordInternal(UserAccounts accounts, Account account) { 556 if (account == null) { 557 return null; 558 } 559 560 synchronized (accounts.cacheLock) { 561 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 562 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, 563 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 564 new String[]{account.name, account.type}, null, null, null); 565 try { 566 if (cursor.moveToNext()) { 567 return cursor.getString(0); 568 } 569 return null; 570 } finally { 571 cursor.close(); 572 } 573 } 574 } 575 576 @Override 577 public String getPreviousName(Account account) { 578 if (Log.isLoggable(TAG, Log.VERBOSE)) { 579 Log.v(TAG, "getPreviousName: " + account 580 + ", caller's uid " + Binder.getCallingUid() 581 + ", pid " + Binder.getCallingPid()); 582 } 583 if (account == null) throw new IllegalArgumentException("account is null"); 584 int userId = UserHandle.getCallingUserId(); 585 long identityToken = clearCallingIdentity(); 586 try { 587 UserAccounts accounts = getUserAccounts(userId); 588 return readPreviousNameInternal(accounts, account); 589 } finally { 590 restoreCallingIdentity(identityToken); 591 } 592 } 593 594 private String readPreviousNameInternal(UserAccounts accounts, Account account) { 595 if (account == null) { 596 return null; 597 } 598 synchronized (accounts.cacheLock) { 599 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account); 600 if (previousNameRef == null) { 601 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 602 Cursor cursor = db.query( 603 TABLE_ACCOUNTS, 604 new String[]{ ACCOUNTS_PREVIOUS_NAME }, 605 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 606 new String[] { account.name, account.type }, 607 null, 608 null, 609 null); 610 try { 611 if (cursor.moveToNext()) { 612 String previousName = cursor.getString(0); 613 previousNameRef = new AtomicReference<String>(previousName); 614 accounts.previousNameCache.put(account, previousNameRef); 615 return previousName; 616 } else { 617 return null; 618 } 619 } finally { 620 cursor.close(); 621 } 622 } else { 623 return previousNameRef.get(); 624 } 625 } 626 } 627 628 @Override 629 public String getUserData(Account account, String key) { 630 final int callingUid = Binder.getCallingUid(); 631 if (Log.isLoggable(TAG, Log.VERBOSE)) { 632 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s", 633 account, key, callingUid, Binder.getCallingPid()); 634 Log.v(TAG, msg); 635 } 636 if (account == null) throw new IllegalArgumentException("account is null"); 637 if (key == null) throw new IllegalArgumentException("key is null"); 638 int userId = UserHandle.getCallingUserId(); 639 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 640 String msg = String.format( 641 "uid %s cannot get user data for accounts of type: %s", 642 callingUid, 643 account.type); 644 throw new SecurityException(msg); 645 } 646 long identityToken = clearCallingIdentity(); 647 try { 648 UserAccounts accounts = getUserAccounts(userId); 649 return readUserDataInternal(accounts, account, key); 650 } finally { 651 restoreCallingIdentity(identityToken); 652 } 653 } 654 655 @Override 656 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) { 657 int callingUid = Binder.getCallingUid(); 658 if (Log.isLoggable(TAG, Log.VERBOSE)) { 659 Log.v(TAG, "getAuthenticatorTypes: " 660 + "for user id " + userId 661 + "caller's uid " + callingUid 662 + ", pid " + Binder.getCallingPid()); 663 } 664 // Only allow the system process to read accounts of other users 665 if (isCrossUser(callingUid, userId)) { 666 throw new SecurityException( 667 String.format( 668 "User %s tying to get authenticator types for %s" , 669 UserHandle.getCallingUserId(), 670 userId)); 671 } 672 673 final long identityToken = clearCallingIdentity(); 674 try { 675 return getAuthenticatorTypesInternal(userId); 676 677 } finally { 678 restoreCallingIdentity(identityToken); 679 } 680 } 681 682 /** 683 * Should only be called inside of a clearCallingIdentity block. 684 */ 685 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) { 686 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> 687 authenticatorCollection = mAuthenticatorCache.getAllServices(userId); 688 AuthenticatorDescription[] types = 689 new AuthenticatorDescription[authenticatorCollection.size()]; 690 int i = 0; 691 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator 692 : authenticatorCollection) { 693 types[i] = authenticator.type; 694 i++; 695 } 696 return types; 697 } 698 699 700 701 private boolean isCrossUser(int callingUid, int userId) { 702 return (userId != UserHandle.getCallingUserId() 703 && callingUid != Process.myUid() 704 && mContext.checkCallingOrSelfPermission( 705 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 706 != PackageManager.PERMISSION_GRANTED); 707 } 708 709 @Override 710 public boolean addAccountExplicitly(Account account, String password, Bundle extras) { 711 final int callingUid = Binder.getCallingUid(); 712 if (Log.isLoggable(TAG, Log.VERBOSE)) { 713 Log.v(TAG, "addAccountExplicitly: " + account 714 + ", caller's uid " + callingUid 715 + ", pid " + Binder.getCallingPid()); 716 } 717 if (account == null) throw new IllegalArgumentException("account is null"); 718 int userId = UserHandle.getCallingUserId(); 719 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 720 String msg = String.format( 721 "uid %s cannot explicitly add accounts of type: %s", 722 callingUid, 723 account.type); 724 throw new SecurityException(msg); 725 } 726 /* 727 * Child users are not allowed to add accounts. Only the accounts that are 728 * shared by the parent profile can be added to child profile. 729 * 730 * TODO: Only allow accounts that were shared to be added by 731 * a limited user. 732 */ 733 734 // fails if the account already exists 735 long identityToken = clearCallingIdentity(); 736 try { 737 UserAccounts accounts = getUserAccounts(userId); 738 return addAccountInternal(accounts, account, password, extras, false, callingUid); 739 } finally { 740 restoreCallingIdentity(identityToken); 741 } 742 } 743 744 @Override 745 public void copyAccountToUser(final IAccountManagerResponse response, final Account account, 746 int userFrom, int userTo) { 747 int callingUid = Binder.getCallingUid(); 748 if (isCrossUser(callingUid, UserHandle.USER_ALL)) { 749 throw new SecurityException("Calling copyAccountToUser requires " 750 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); 751 } 752 final UserAccounts fromAccounts = getUserAccounts(userFrom); 753 final UserAccounts toAccounts = getUserAccounts(userTo); 754 if (fromAccounts == null || toAccounts == null) { 755 if (response != null) { 756 Bundle result = new Bundle(); 757 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); 758 try { 759 response.onResult(result); 760 } catch (RemoteException e) { 761 Slog.w(TAG, "Failed to report error back to the client." + e); 762 } 763 } 764 return; 765 } 766 767 Slog.d(TAG, "Copying account " + account.name 768 + " from user " + userFrom + " to user " + userTo); 769 long identityToken = clearCallingIdentity(); 770 try { 771 new Session(fromAccounts, response, account.type, false, 772 false /* stripAuthTokenFromResult */, account.name, 773 false /* authDetailsRequired */) { 774 @Override 775 protected String toDebugString(long now) { 776 return super.toDebugString(now) + ", getAccountCredentialsForClone" 777 + ", " + account.type; 778 } 779 780 @Override 781 public void run() throws RemoteException { 782 mAuthenticator.getAccountCredentialsForCloning(this, account); 783 } 784 785 @Override 786 public void onResult(Bundle result) { 787 if (result != null 788 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 789 // Create a Session for the target user and pass in the bundle 790 completeCloningAccount(response, result, account, toAccounts); 791 } else { 792 super.onResult(result); 793 } 794 } 795 }.bind(); 796 } finally { 797 restoreCallingIdentity(identityToken); 798 } 799 } 800 801 @Override 802 public boolean accountAuthenticated(final Account account) { 803 final int callingUid = Binder.getCallingUid(); 804 if (Log.isLoggable(TAG, Log.VERBOSE)) { 805 String msg = String.format( 806 "accountAuthenticated( account: %s, callerUid: %s)", 807 account, 808 callingUid); 809 Log.v(TAG, msg); 810 } 811 if (account == null) { 812 throw new IllegalArgumentException("account is null"); 813 } 814 int userId = UserHandle.getCallingUserId(); 815 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 816 String msg = String.format( 817 "uid %s cannot notify authentication for accounts of type: %s", 818 callingUid, 819 account.type); 820 throw new SecurityException(msg); 821 } 822 823 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { 824 return false; 825 } 826 827 long identityToken = clearCallingIdentity(); 828 try { 829 UserAccounts accounts = getUserAccounts(userId); 830 return updateLastAuthenticatedTime(account); 831 } finally { 832 restoreCallingIdentity(identityToken); 833 } 834 } 835 836 private boolean updateLastAuthenticatedTime(Account account) { 837 final UserAccounts accounts = getUserAccountsForCaller(); 838 synchronized (accounts.cacheLock) { 839 final ContentValues values = new ContentValues(); 840 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis()); 841 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 842 int i = db.update( 843 TABLE_ACCOUNTS, 844 values, 845 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 846 new String[] { 847 account.name, account.type 848 }); 849 if (i > 0) { 850 return true; 851 } 852 } 853 return false; 854 } 855 856 private void completeCloningAccount(IAccountManagerResponse response, 857 final Bundle accountCredentials, final Account account, final UserAccounts targetUser) { 858 long id = clearCallingIdentity(); 859 try { 860 new Session(targetUser, response, account.type, false, 861 false /* stripAuthTokenFromResult */, account.name, 862 false /* authDetailsRequired */) { 863 @Override 864 protected String toDebugString(long now) { 865 return super.toDebugString(now) + ", getAccountCredentialsForClone" 866 + ", " + account.type; 867 } 868 869 @Override 870 public void run() throws RemoteException { 871 // Confirm that the owner's account still exists before this step. 872 UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); 873 synchronized (owner.cacheLock) { 874 for (Account acc : getAccounts(UserHandle.USER_OWNER, 875 mContext.getOpPackageName())) { 876 if (acc.equals(account)) { 877 mAuthenticator.addAccountFromCredentials( 878 this, account, accountCredentials); 879 break; 880 } 881 } 882 } 883 } 884 885 @Override 886 public void onResult(Bundle result) { 887 // TODO: Anything to do if if succedded? 888 // TODO: If it failed: Show error notification? Should we remove the shadow 889 // account to avoid retries? 890 super.onResult(result); 891 } 892 893 @Override 894 public void onError(int errorCode, String errorMessage) { 895 super.onError(errorCode, errorMessage); 896 // TODO: Show error notification to user 897 // TODO: Should we remove the shadow account so that it doesn't keep trying? 898 } 899 900 }.bind(); 901 } finally { 902 restoreCallingIdentity(id); 903 } 904 } 905 906 private boolean addAccountInternal(UserAccounts accounts, Account account, String password, 907 Bundle extras, boolean restricted, int callingUid) { 908 if (account == null) { 909 return false; 910 } 911 synchronized (accounts.cacheLock) { 912 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 913 db.beginTransaction(); 914 try { 915 long numMatches = DatabaseUtils.longForQuery(db, 916 "select count(*) from " + TABLE_ACCOUNTS 917 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 918 new String[]{account.name, account.type}); 919 if (numMatches > 0) { 920 Log.w(TAG, "insertAccountIntoDatabase: " + account 921 + ", skipping since the account already exists"); 922 return false; 923 } 924 ContentValues values = new ContentValues(); 925 values.put(ACCOUNTS_NAME, account.name); 926 values.put(ACCOUNTS_TYPE, account.type); 927 values.put(ACCOUNTS_PASSWORD, password); 928 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis()); 929 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); 930 if (accountId < 0) { 931 Log.w(TAG, "insertAccountIntoDatabase: " + account 932 + ", skipping the DB insert failed"); 933 return false; 934 } 935 if (extras != null) { 936 for (String key : extras.keySet()) { 937 final String value = extras.getString(key); 938 if (insertExtraLocked(db, accountId, key, value) < 0) { 939 Log.w(TAG, "insertAccountIntoDatabase: " + account 940 + ", skipping since insertExtra failed for key " + key); 941 return false; 942 } 943 } 944 } 945 db.setTransactionSuccessful(); 946 947 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId, 948 accounts, callingUid); 949 950 insertAccountIntoCacheLocked(accounts, account); 951 } finally { 952 db.endTransaction(); 953 } 954 sendAccountsChangedBroadcast(accounts.userId); 955 } 956 if (accounts.userId == UserHandle.USER_OWNER) { 957 addAccountToLimitedUsers(account); 958 } 959 return true; 960 } 961 962 /** 963 * Adds the account to all limited users as shared accounts. If the user is currently 964 * running, then clone the account too. 965 * @param account the account to share with limited users 966 */ 967 private void addAccountToLimitedUsers(Account account) { 968 List<UserInfo> users = getUserManager().getUsers(); 969 for (UserInfo user : users) { 970 if (user.isRestricted()) { 971 addSharedAccountAsUser(account, user.id); 972 try { 973 if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) { 974 mMessageHandler.sendMessage(mMessageHandler.obtainMessage( 975 MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id, 976 account)); 977 } 978 } catch (RemoteException re) { 979 // Shouldn't happen 980 } 981 } 982 } 983 } 984 985 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { 986 ContentValues values = new ContentValues(); 987 values.put(EXTRAS_KEY, key); 988 values.put(EXTRAS_ACCOUNTS_ID, accountId); 989 values.put(EXTRAS_VALUE, value); 990 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); 991 } 992 993 @Override 994 public void hasFeatures(IAccountManagerResponse response, 995 Account account, String[] features, String opPackageName) { 996 int callingUid = Binder.getCallingUid(); 997 if (Log.isLoggable(TAG, Log.VERBOSE)) { 998 Log.v(TAG, "hasFeatures: " + account 999 + ", response " + response 1000 + ", features " + stringArrayToString(features) 1001 + ", caller's uid " + callingUid 1002 + ", pid " + Binder.getCallingPid()); 1003 } 1004 if (response == null) throw new IllegalArgumentException("response is null"); 1005 if (account == null) throw new IllegalArgumentException("account is null"); 1006 if (features == null) throw new IllegalArgumentException("features is null"); 1007 int userId = UserHandle.getCallingUserId(); 1008 checkReadAccountsPermitted(callingUid, account.type, userId, 1009 opPackageName); 1010 1011 long identityToken = clearCallingIdentity(); 1012 try { 1013 UserAccounts accounts = getUserAccounts(userId); 1014 new TestFeaturesSession(accounts, response, account, features).bind(); 1015 } finally { 1016 restoreCallingIdentity(identityToken); 1017 } 1018 } 1019 1020 private class TestFeaturesSession extends Session { 1021 private final String[] mFeatures; 1022 private final Account mAccount; 1023 1024 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, 1025 Account account, String[] features) { 1026 super(accounts, response, account.type, false /* expectActivityLaunch */, 1027 true /* stripAuthTokenFromResult */, account.name, 1028 false /* authDetailsRequired */); 1029 mFeatures = features; 1030 mAccount = account; 1031 } 1032 1033 @Override 1034 public void run() throws RemoteException { 1035 try { 1036 mAuthenticator.hasFeatures(this, mAccount, mFeatures); 1037 } catch (RemoteException e) { 1038 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 1039 } 1040 } 1041 1042 @Override 1043 public void onResult(Bundle result) { 1044 IAccountManagerResponse response = getResponseAndClose(); 1045 if (response != null) { 1046 try { 1047 if (result == null) { 1048 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 1049 return; 1050 } 1051 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1052 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1053 + response); 1054 } 1055 final Bundle newResult = new Bundle(); 1056 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 1057 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 1058 response.onResult(newResult); 1059 } catch (RemoteException e) { 1060 // if the caller is dead then there is no one to care about remote exceptions 1061 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1062 Log.v(TAG, "failure while notifying response", e); 1063 } 1064 } 1065 } 1066 } 1067 1068 @Override 1069 protected String toDebugString(long now) { 1070 return super.toDebugString(now) + ", hasFeatures" 1071 + ", " + mAccount 1072 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 1073 } 1074 } 1075 1076 @Override 1077 public void renameAccount( 1078 IAccountManagerResponse response, Account accountToRename, String newName) { 1079 final int callingUid = Binder.getCallingUid(); 1080 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1081 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName 1082 + ", caller's uid " + callingUid 1083 + ", pid " + Binder.getCallingPid()); 1084 } 1085 if (accountToRename == null) throw new IllegalArgumentException("account is null"); 1086 int userId = UserHandle.getCallingUserId(); 1087 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { 1088 String msg = String.format( 1089 "uid %s cannot rename accounts of type: %s", 1090 callingUid, 1091 accountToRename.type); 1092 throw new SecurityException(msg); 1093 } 1094 long identityToken = clearCallingIdentity(); 1095 try { 1096 UserAccounts accounts = getUserAccounts(userId); 1097 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName); 1098 Bundle result = new Bundle(); 1099 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); 1100 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); 1101 try { 1102 response.onResult(result); 1103 } catch (RemoteException e) { 1104 Log.w(TAG, e.getMessage()); 1105 } 1106 } finally { 1107 restoreCallingIdentity(identityToken); 1108 } 1109 } 1110 1111 private Account renameAccountInternal( 1112 UserAccounts accounts, Account accountToRename, String newName) { 1113 Account resultAccount = null; 1114 /* 1115 * Cancel existing notifications. Let authenticators 1116 * re-post notifications as required. But we don't know if 1117 * the authenticators have bound their notifications to 1118 * now stale account name data. 1119 * 1120 * With a rename api, we might not need to do this anymore but it 1121 * shouldn't hurt. 1122 */ 1123 cancelNotification( 1124 getSigninRequiredNotificationId(accounts, accountToRename), 1125 new UserHandle(accounts.userId)); 1126 synchronized(accounts.credentialsPermissionNotificationIds) { 1127 for (Pair<Pair<Account, String>, Integer> pair: 1128 accounts.credentialsPermissionNotificationIds.keySet()) { 1129 if (accountToRename.equals(pair.first.first)) { 1130 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1131 cancelNotification(id, new UserHandle(accounts.userId)); 1132 } 1133 } 1134 } 1135 synchronized (accounts.cacheLock) { 1136 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1137 db.beginTransaction(); 1138 boolean isSuccessful = false; 1139 Account renamedAccount = new Account(newName, accountToRename.type); 1140 try { 1141 final ContentValues values = new ContentValues(); 1142 values.put(ACCOUNTS_NAME, newName); 1143 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name); 1144 final long accountId = getAccountIdLocked(db, accountToRename); 1145 if (accountId >= 0) { 1146 final String[] argsAccountId = { String.valueOf(accountId) }; 1147 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 1148 db.setTransactionSuccessful(); 1149 isSuccessful = true; 1150 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId, 1151 accounts); 1152 } 1153 } finally { 1154 db.endTransaction(); 1155 if (isSuccessful) { 1156 /* 1157 * Database transaction was successful. Clean up cached 1158 * data associated with the account in the user profile. 1159 */ 1160 insertAccountIntoCacheLocked(accounts, renamedAccount); 1161 /* 1162 * Extract the data and token caches before removing the 1163 * old account to preserve the user data associated with 1164 * the account. 1165 */ 1166 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename); 1167 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename); 1168 removeAccountFromCacheLocked(accounts, accountToRename); 1169 /* 1170 * Update the cached data associated with the renamed 1171 * account. 1172 */ 1173 accounts.userDataCache.put(renamedAccount, tmpData); 1174 accounts.authTokenCache.put(renamedAccount, tmpTokens); 1175 accounts.previousNameCache.put( 1176 renamedAccount, 1177 new AtomicReference<String>(accountToRename.name)); 1178 resultAccount = renamedAccount; 1179 1180 if (accounts.userId == UserHandle.USER_OWNER) { 1181 /* 1182 * Owner's account was renamed, rename the account for 1183 * those users with which the account was shared. 1184 */ 1185 List<UserInfo> users = mUserManager.getUsers(true); 1186 for (UserInfo user : users) { 1187 if (!user.isPrimary() && user.isRestricted()) { 1188 renameSharedAccountAsUser(accountToRename, newName, user.id); 1189 } 1190 } 1191 } 1192 sendAccountsChangedBroadcast(accounts.userId); 1193 } 1194 } 1195 } 1196 return resultAccount; 1197 } 1198 1199 @Override 1200 public void removeAccount(IAccountManagerResponse response, Account account, 1201 boolean expectActivityLaunch) { 1202 removeAccountAsUser( 1203 response, 1204 account, 1205 expectActivityLaunch, 1206 UserHandle.getCallingUserId()); 1207 } 1208 1209 @Override 1210 public void removeAccountAsUser(IAccountManagerResponse response, Account account, 1211 boolean expectActivityLaunch, int userId) { 1212 final int callingUid = Binder.getCallingUid(); 1213 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1214 Log.v(TAG, "removeAccount: " + account 1215 + ", response " + response 1216 + ", caller's uid " + callingUid 1217 + ", pid " + Binder.getCallingPid() 1218 + ", for user id " + userId); 1219 } 1220 if (response == null) throw new IllegalArgumentException("response is null"); 1221 if (account == null) throw new IllegalArgumentException("account is null"); 1222 // Only allow the system process to modify accounts of other users 1223 if (isCrossUser(callingUid, userId)) { 1224 throw new SecurityException( 1225 String.format( 1226 "User %s tying remove account for %s" , 1227 UserHandle.getCallingUserId(), 1228 userId)); 1229 } 1230 /* 1231 * Only the system or authenticator should be allowed to remove accounts for that 1232 * authenticator. This will let users remove accounts (via Settings in the system) but not 1233 * arbitrary applications (like competing authenticators). 1234 */ 1235 UserHandle user = new UserHandle(userId); 1236 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) 1237 && !isSystemUid(callingUid)) { 1238 String msg = String.format( 1239 "uid %s cannot remove accounts of type: %s", 1240 callingUid, 1241 account.type); 1242 throw new SecurityException(msg); 1243 } 1244 if (!canUserModifyAccounts(userId)) { 1245 try { 1246 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1247 "User cannot modify accounts"); 1248 } catch (RemoteException re) { 1249 } 1250 return; 1251 } 1252 if (!canUserModifyAccountsForType(userId, account.type)) { 1253 try { 1254 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1255 "User cannot modify accounts of this type (policy)."); 1256 } catch (RemoteException re) { 1257 } 1258 return; 1259 } 1260 long identityToken = clearCallingIdentity(); 1261 UserAccounts accounts = getUserAccounts(userId); 1262 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 1263 synchronized(accounts.credentialsPermissionNotificationIds) { 1264 for (Pair<Pair<Account, String>, Integer> pair: 1265 accounts.credentialsPermissionNotificationIds.keySet()) { 1266 if (account.equals(pair.first.first)) { 1267 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1268 cancelNotification(id, user); 1269 } 1270 } 1271 } 1272 1273 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS); 1274 1275 try { 1276 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); 1277 } finally { 1278 restoreCallingIdentity(identityToken); 1279 } 1280 } 1281 1282 @Override 1283 public boolean removeAccountExplicitly(Account account) { 1284 final int callingUid = Binder.getCallingUid(); 1285 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1286 Log.v(TAG, "removeAccountExplicitly: " + account 1287 + ", caller's uid " + callingUid 1288 + ", pid " + Binder.getCallingPid()); 1289 } 1290 int userId = Binder.getCallingUserHandle().getIdentifier(); 1291 if (account == null) { 1292 /* 1293 * Null accounts should result in returning false, as per 1294 * AccountManage.addAccountExplicitly(...) java doc. 1295 */ 1296 Log.e(TAG, "account is null"); 1297 return false; 1298 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1299 String msg = String.format( 1300 "uid %s cannot explicitly add accounts of type: %s", 1301 callingUid, 1302 account.type); 1303 throw new SecurityException(msg); 1304 } 1305 UserAccounts accounts = getUserAccountsForCaller(); 1306 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { 1307 return false; 1308 } 1309 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS); 1310 long identityToken = clearCallingIdentity(); 1311 try { 1312 return removeAccountInternal(accounts, account); 1313 } finally { 1314 restoreCallingIdentity(identityToken); 1315 } 1316 } 1317 1318 private class RemoveAccountSession extends Session { 1319 final Account mAccount; 1320 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, 1321 Account account, boolean expectActivityLaunch) { 1322 super(accounts, response, account.type, expectActivityLaunch, 1323 true /* stripAuthTokenFromResult */, account.name, 1324 false /* authDetailsRequired */); 1325 mAccount = account; 1326 } 1327 1328 @Override 1329 protected String toDebugString(long now) { 1330 return super.toDebugString(now) + ", removeAccount" 1331 + ", account " + mAccount; 1332 } 1333 1334 @Override 1335 public void run() throws RemoteException { 1336 mAuthenticator.getAccountRemovalAllowed(this, mAccount); 1337 } 1338 1339 @Override 1340 public void onResult(Bundle result) { 1341 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) 1342 && !result.containsKey(AccountManager.KEY_INTENT)) { 1343 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); 1344 if (removalAllowed) { 1345 removeAccountInternal(mAccounts, mAccount); 1346 } 1347 IAccountManagerResponse response = getResponseAndClose(); 1348 if (response != null) { 1349 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1350 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1351 + response); 1352 } 1353 Bundle result2 = new Bundle(); 1354 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); 1355 try { 1356 response.onResult(result2); 1357 } catch (RemoteException e) { 1358 // ignore 1359 } 1360 } 1361 } 1362 super.onResult(result); 1363 } 1364 } 1365 1366 /* For testing */ 1367 protected void removeAccountInternal(Account account) { 1368 removeAccountInternal(getUserAccountsForCaller(), account); 1369 } 1370 1371 private boolean removeAccountInternal(UserAccounts accounts, Account account) { 1372 int deleted; 1373 synchronized (accounts.cacheLock) { 1374 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1375 final long accountId = getAccountIdLocked(db, account); 1376 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE 1377 + "=?", 1378 new String[]{account.name, account.type}); 1379 removeAccountFromCacheLocked(accounts, account); 1380 sendAccountsChangedBroadcast(accounts.userId); 1381 1382 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts); 1383 } 1384 if (accounts.userId == UserHandle.USER_OWNER) { 1385 // Owner's account was removed, remove from any users that are sharing 1386 // this account. 1387 int callingUid = getCallingUid(); 1388 long id = Binder.clearCallingIdentity(); 1389 try { 1390 List<UserInfo> users = mUserManager.getUsers(true); 1391 for (UserInfo user : users) { 1392 if (!user.isPrimary() && user.isRestricted()) { 1393 removeSharedAccountAsUser(account, user.id, callingUid); 1394 } 1395 } 1396 } finally { 1397 Binder.restoreCallingIdentity(id); 1398 } 1399 } 1400 return (deleted > 0); 1401 } 1402 1403 @Override 1404 public void invalidateAuthToken(String accountType, String authToken) { 1405 int callerUid = Binder.getCallingUid(); 1406 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1407 Log.v(TAG, "invalidateAuthToken: accountType " + accountType 1408 + ", caller's uid " + callerUid 1409 + ", pid " + Binder.getCallingPid()); 1410 } 1411 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1412 if (authToken == null) throw new IllegalArgumentException("authToken is null"); 1413 int userId = UserHandle.getCallingUserId(); 1414 long identityToken = clearCallingIdentity(); 1415 try { 1416 UserAccounts accounts = getUserAccounts(userId); 1417 synchronized (accounts.cacheLock) { 1418 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1419 db.beginTransaction(); 1420 try { 1421 invalidateAuthTokenLocked(accounts, db, accountType, authToken); 1422 invalidateCustomTokenLocked(accounts, accountType, authToken); 1423 db.setTransactionSuccessful(); 1424 } finally { 1425 db.endTransaction(); 1426 } 1427 } 1428 } finally { 1429 restoreCallingIdentity(identityToken); 1430 } 1431 } 1432 1433 private void invalidateCustomTokenLocked( 1434 UserAccounts accounts, 1435 String accountType, 1436 String authToken) { 1437 if (authToken == null || accountType == null) { 1438 return; 1439 } 1440 // Also wipe out cached token in memory. 1441 accounts.accountTokenCaches.remove(accountType, authToken); 1442 } 1443 1444 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, 1445 String accountType, String authToken) { 1446 if (authToken == null || accountType == null) { 1447 return; 1448 } 1449 Cursor cursor = db.rawQuery( 1450 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID 1451 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 1452 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE 1453 + " FROM " + TABLE_ACCOUNTS 1454 + " JOIN " + TABLE_AUTHTOKENS 1455 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1456 + " = " + AUTHTOKENS_ACCOUNTS_ID 1457 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND " 1458 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", 1459 new String[]{authToken, accountType}); 1460 try { 1461 while (cursor.moveToNext()) { 1462 long authTokenId = cursor.getLong(0); 1463 String accountName = cursor.getString(1); 1464 String authTokenType = cursor.getString(2); 1465 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); 1466 writeAuthTokenIntoCacheLocked( 1467 accounts, 1468 db, 1469 new Account(accountName, accountType), 1470 authTokenType, 1471 null); 1472 } 1473 } finally { 1474 cursor.close(); 1475 } 1476 } 1477 1478 private void saveCachedToken( 1479 UserAccounts accounts, 1480 Account account, 1481 String callerPkg, 1482 byte[] callerSigDigest, 1483 String tokenType, 1484 String token, 1485 long expiryMillis) { 1486 1487 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) { 1488 return; 1489 } 1490 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1491 new UserHandle(accounts.userId)); 1492 synchronized (accounts.cacheLock) { 1493 accounts.accountTokenCaches.put( 1494 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis); 1495 } 1496 } 1497 1498 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, 1499 String authToken) { 1500 if (account == null || type == null) { 1501 return false; 1502 } 1503 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1504 new UserHandle(accounts.userId)); 1505 synchronized (accounts.cacheLock) { 1506 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1507 db.beginTransaction(); 1508 try { 1509 long accountId = getAccountIdLocked(db, account); 1510 if (accountId < 0) { 1511 return false; 1512 } 1513 db.delete(TABLE_AUTHTOKENS, 1514 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", 1515 new String[]{type}); 1516 ContentValues values = new ContentValues(); 1517 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); 1518 values.put(AUTHTOKENS_TYPE, type); 1519 values.put(AUTHTOKENS_AUTHTOKEN, authToken); 1520 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { 1521 db.setTransactionSuccessful(); 1522 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); 1523 return true; 1524 } 1525 return false; 1526 } finally { 1527 db.endTransaction(); 1528 } 1529 } 1530 } 1531 1532 @Override 1533 public String peekAuthToken(Account account, String authTokenType) { 1534 final int callingUid = Binder.getCallingUid(); 1535 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1536 Log.v(TAG, "peekAuthToken: " + account 1537 + ", authTokenType " + authTokenType 1538 + ", caller's uid " + callingUid 1539 + ", pid " + Binder.getCallingPid()); 1540 } 1541 if (account == null) throw new IllegalArgumentException("account is null"); 1542 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1543 int userId = UserHandle.getCallingUserId(); 1544 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1545 String msg = String.format( 1546 "uid %s cannot peek the authtokens associated with accounts of type: %s", 1547 callingUid, 1548 account.type); 1549 throw new SecurityException(msg); 1550 } 1551 long identityToken = clearCallingIdentity(); 1552 try { 1553 UserAccounts accounts = getUserAccounts(userId); 1554 return readAuthTokenInternal(accounts, account, authTokenType); 1555 } finally { 1556 restoreCallingIdentity(identityToken); 1557 } 1558 } 1559 1560 @Override 1561 public void setAuthToken(Account account, String authTokenType, String authToken) { 1562 final int callingUid = Binder.getCallingUid(); 1563 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1564 Log.v(TAG, "setAuthToken: " + account 1565 + ", authTokenType " + authTokenType 1566 + ", caller's uid " + callingUid 1567 + ", pid " + Binder.getCallingPid()); 1568 } 1569 if (account == null) throw new IllegalArgumentException("account is null"); 1570 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1571 int userId = UserHandle.getCallingUserId(); 1572 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1573 String msg = String.format( 1574 "uid %s cannot set auth tokens associated with accounts of type: %s", 1575 callingUid, 1576 account.type); 1577 throw new SecurityException(msg); 1578 } 1579 long identityToken = clearCallingIdentity(); 1580 try { 1581 UserAccounts accounts = getUserAccounts(userId); 1582 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); 1583 } finally { 1584 restoreCallingIdentity(identityToken); 1585 } 1586 } 1587 1588 @Override 1589 public void setPassword(Account account, String password) { 1590 final int callingUid = Binder.getCallingUid(); 1591 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1592 Log.v(TAG, "setAuthToken: " + account 1593 + ", caller's uid " + callingUid 1594 + ", pid " + Binder.getCallingPid()); 1595 } 1596 if (account == null) throw new IllegalArgumentException("account is null"); 1597 int userId = UserHandle.getCallingUserId(); 1598 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1599 String msg = String.format( 1600 "uid %s cannot set secrets for accounts of type: %s", 1601 callingUid, 1602 account.type); 1603 throw new SecurityException(msg); 1604 } 1605 long identityToken = clearCallingIdentity(); 1606 try { 1607 UserAccounts accounts = getUserAccounts(userId); 1608 setPasswordInternal(accounts, account, password, callingUid); 1609 } finally { 1610 restoreCallingIdentity(identityToken); 1611 } 1612 } 1613 1614 private void setPasswordInternal(UserAccounts accounts, Account account, String password, 1615 int callingUid) { 1616 if (account == null) { 1617 return; 1618 } 1619 synchronized (accounts.cacheLock) { 1620 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1621 db.beginTransaction(); 1622 try { 1623 final ContentValues values = new ContentValues(); 1624 values.put(ACCOUNTS_PASSWORD, password); 1625 final long accountId = getAccountIdLocked(db, account); 1626 if (accountId >= 0) { 1627 final String[] argsAccountId = {String.valueOf(accountId)}; 1628 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 1629 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); 1630 accounts.authTokenCache.remove(account); 1631 accounts.accountTokenCaches.remove(account); 1632 db.setTransactionSuccessful(); 1633 1634 String action = (password == null || password.length() == 0) ? 1635 DebugDbHelper.ACTION_CLEAR_PASSWORD 1636 : DebugDbHelper.ACTION_SET_PASSWORD; 1637 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid); 1638 } 1639 } finally { 1640 db.endTransaction(); 1641 } 1642 sendAccountsChangedBroadcast(accounts.userId); 1643 } 1644 } 1645 1646 private void sendAccountsChangedBroadcast(int userId) { 1647 Log.i(TAG, "the accounts changed, sending broadcast of " 1648 + ACCOUNTS_CHANGED_INTENT.getAction()); 1649 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); 1650 } 1651 1652 @Override 1653 public void clearPassword(Account account) { 1654 final int callingUid = Binder.getCallingUid(); 1655 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1656 Log.v(TAG, "clearPassword: " + account 1657 + ", caller's uid " + callingUid 1658 + ", pid " + Binder.getCallingPid()); 1659 } 1660 if (account == null) throw new IllegalArgumentException("account is null"); 1661 int userId = UserHandle.getCallingUserId(); 1662 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1663 String msg = String.format( 1664 "uid %s cannot clear passwords for accounts of type: %s", 1665 callingUid, 1666 account.type); 1667 throw new SecurityException(msg); 1668 } 1669 long identityToken = clearCallingIdentity(); 1670 try { 1671 UserAccounts accounts = getUserAccounts(userId); 1672 setPasswordInternal(accounts, account, null, callingUid); 1673 } finally { 1674 restoreCallingIdentity(identityToken); 1675 } 1676 } 1677 1678 @Override 1679 public void setUserData(Account account, String key, String value) { 1680 final int callingUid = Binder.getCallingUid(); 1681 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1682 Log.v(TAG, "setUserData: " + account 1683 + ", key " + key 1684 + ", caller's uid " + callingUid 1685 + ", pid " + Binder.getCallingPid()); 1686 } 1687 if (key == null) throw new IllegalArgumentException("key is null"); 1688 if (account == null) throw new IllegalArgumentException("account is null"); 1689 int userId = UserHandle.getCallingUserId(); 1690 if (!isAccountManagedByCaller(account.type, callingUid, userId)) { 1691 String msg = String.format( 1692 "uid %s cannot set user data for accounts of type: %s", 1693 callingUid, 1694 account.type); 1695 throw new SecurityException(msg); 1696 } 1697 long identityToken = clearCallingIdentity(); 1698 try { 1699 UserAccounts accounts = getUserAccounts(userId); 1700 setUserdataInternal(accounts, account, key, value); 1701 } finally { 1702 restoreCallingIdentity(identityToken); 1703 } 1704 } 1705 1706 private void setUserdataInternal(UserAccounts accounts, Account account, String key, 1707 String value) { 1708 if (account == null || key == null) { 1709 return; 1710 } 1711 synchronized (accounts.cacheLock) { 1712 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1713 db.beginTransaction(); 1714 try { 1715 long accountId = getAccountIdLocked(db, account); 1716 if (accountId < 0) { 1717 return; 1718 } 1719 long extrasId = getExtrasIdLocked(db, accountId, key); 1720 if (extrasId < 0 ) { 1721 extrasId = insertExtraLocked(db, accountId, key, value); 1722 if (extrasId < 0) { 1723 return; 1724 } 1725 } else { 1726 ContentValues values = new ContentValues(); 1727 values.put(EXTRAS_VALUE, value); 1728 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { 1729 return; 1730 } 1731 1732 } 1733 writeUserDataIntoCacheLocked(accounts, db, account, key, value); 1734 db.setTransactionSuccessful(); 1735 } finally { 1736 db.endTransaction(); 1737 } 1738 } 1739 } 1740 1741 private void onResult(IAccountManagerResponse response, Bundle result) { 1742 if (result == null) { 1743 Log.e(TAG, "the result is unexpectedly null", new Exception()); 1744 } 1745 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1746 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1747 + response); 1748 } 1749 try { 1750 response.onResult(result); 1751 } catch (RemoteException e) { 1752 // if the caller is dead then there is no one to care about remote 1753 // exceptions 1754 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1755 Log.v(TAG, "failure while notifying response", e); 1756 } 1757 } 1758 } 1759 1760 @Override 1761 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, 1762 final String authTokenType) 1763 throws RemoteException { 1764 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1765 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1766 1767 final int callingUid = getCallingUid(); 1768 clearCallingIdentity(); 1769 if (callingUid != Process.SYSTEM_UID) { 1770 throw new SecurityException("can only call from system"); 1771 } 1772 int userId = UserHandle.getUserId(callingUid); 1773 long identityToken = clearCallingIdentity(); 1774 try { 1775 UserAccounts accounts = getUserAccounts(userId); 1776 new Session(accounts, response, accountType, false /* expectActivityLaunch */, 1777 false /* stripAuthTokenFromResult */, null /* accountName */, 1778 false /* authDetailsRequired */) { 1779 @Override 1780 protected String toDebugString(long now) { 1781 return super.toDebugString(now) + ", getAuthTokenLabel" 1782 + ", " + accountType 1783 + ", authTokenType " + authTokenType; 1784 } 1785 1786 @Override 1787 public void run() throws RemoteException { 1788 mAuthenticator.getAuthTokenLabel(this, authTokenType); 1789 } 1790 1791 @Override 1792 public void onResult(Bundle result) { 1793 if (result != null) { 1794 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); 1795 Bundle bundle = new Bundle(); 1796 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); 1797 super.onResult(bundle); 1798 return; 1799 } else { 1800 super.onResult(result); 1801 } 1802 } 1803 }.bind(); 1804 } finally { 1805 restoreCallingIdentity(identityToken); 1806 } 1807 } 1808 1809 @Override 1810 public void getAuthToken( 1811 IAccountManagerResponse response, 1812 final Account account, 1813 final String authTokenType, 1814 final boolean notifyOnAuthFailure, 1815 final boolean expectActivityLaunch, 1816 final Bundle loginOptions) { 1817 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1818 Log.v(TAG, "getAuthToken: " + account 1819 + ", response " + response 1820 + ", authTokenType " + authTokenType 1821 + ", notifyOnAuthFailure " + notifyOnAuthFailure 1822 + ", expectActivityLaunch " + expectActivityLaunch 1823 + ", caller's uid " + Binder.getCallingUid() 1824 + ", pid " + Binder.getCallingPid()); 1825 } 1826 if (response == null) throw new IllegalArgumentException("response is null"); 1827 try { 1828 if (account == null) { 1829 Slog.w(TAG, "getAuthToken called with null account"); 1830 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null"); 1831 return; 1832 } 1833 if (authTokenType == null) { 1834 Slog.w(TAG, "getAuthToken called with null authTokenType"); 1835 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null"); 1836 return; 1837 } 1838 } catch (RemoteException e) { 1839 Slog.w(TAG, "Failed to report error back to the client." + e); 1840 return; 1841 } 1842 int userId = UserHandle.getCallingUserId(); 1843 long ident = Binder.clearCallingIdentity(); 1844 final UserAccounts accounts; 1845 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 1846 try { 1847 accounts = getUserAccounts(userId); 1848 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 1849 AuthenticatorDescription.newKey(account.type), accounts.userId); 1850 } finally { 1851 Binder.restoreCallingIdentity(ident); 1852 } 1853 1854 final boolean customTokens = 1855 authenticatorInfo != null && authenticatorInfo.type.customTokens; 1856 1857 // skip the check if customTokens 1858 final int callerUid = Binder.getCallingUid(); 1859 final boolean permissionGranted = 1860 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); 1861 1862 // Get the calling package. We will use it for the purpose of caching. 1863 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); 1864 List<String> callerOwnedPackageNames; 1865 ident = Binder.clearCallingIdentity(); 1866 try { 1867 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid)); 1868 } finally { 1869 Binder.restoreCallingIdentity(ident); 1870 } 1871 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) { 1872 String msg = String.format( 1873 "Uid %s is attempting to illegally masquerade as package %s!", 1874 callerUid, 1875 callerPkg); 1876 throw new SecurityException(msg); 1877 } 1878 1879 // let authenticator know the identity of the caller 1880 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); 1881 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); 1882 1883 if (notifyOnAuthFailure) { 1884 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); 1885 } 1886 1887 long identityToken = clearCallingIdentity(); 1888 try { 1889 // Distill the caller's package signatures into a single digest. 1890 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg); 1891 1892 // if the caller has permission, do the peek. otherwise go the more expensive 1893 // route of starting a Session 1894 if (!customTokens && permissionGranted) { 1895 String authToken = readAuthTokenInternal(accounts, account, authTokenType); 1896 if (authToken != null) { 1897 Bundle result = new Bundle(); 1898 result.putString(AccountManager.KEY_AUTHTOKEN, authToken); 1899 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 1900 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 1901 onResult(response, result); 1902 return; 1903 } 1904 } 1905 1906 if (customTokens) { 1907 /* 1908 * Look up tokens in the new cache only if the loginOptions don't have parameters 1909 * outside of those expected to be injected by the AccountManager, e.g. 1910 * ANDORID_PACKAGE_NAME. 1911 */ 1912 String token = readCachedTokenInternal( 1913 accounts, 1914 account, 1915 authTokenType, 1916 callerPkg, 1917 callerPkgSigDigest); 1918 if (token != null) { 1919 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1920 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); 1921 } 1922 Bundle result = new Bundle(); 1923 result.putString(AccountManager.KEY_AUTHTOKEN, token); 1924 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 1925 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 1926 onResult(response, result); 1927 return; 1928 } 1929 } 1930 1931 new Session(accounts, response, account.type, expectActivityLaunch, 1932 false /* stripAuthTokenFromResult */, account.name, 1933 false /* authDetailsRequired */) { 1934 @Override 1935 protected String toDebugString(long now) { 1936 if (loginOptions != null) loginOptions.keySet(); 1937 return super.toDebugString(now) + ", getAuthToken" 1938 + ", " + account 1939 + ", authTokenType " + authTokenType 1940 + ", loginOptions " + loginOptions 1941 + ", notifyOnAuthFailure " + notifyOnAuthFailure; 1942 } 1943 1944 @Override 1945 public void run() throws RemoteException { 1946 // If the caller doesn't have permission then create and return the 1947 // "grant permission" intent instead of the "getAuthToken" intent. 1948 if (!permissionGranted) { 1949 mAuthenticator.getAuthTokenLabel(this, authTokenType); 1950 } else { 1951 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); 1952 } 1953 } 1954 1955 @Override 1956 public void onResult(Bundle result) { 1957 if (result != null) { 1958 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { 1959 Intent intent = newGrantCredentialsPermissionIntent( 1960 account, 1961 callerUid, 1962 new AccountAuthenticatorResponse(this), 1963 authTokenType); 1964 Bundle bundle = new Bundle(); 1965 bundle.putParcelable(AccountManager.KEY_INTENT, intent); 1966 onResult(bundle); 1967 return; 1968 } 1969 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 1970 if (authToken != null) { 1971 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); 1972 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 1973 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { 1974 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 1975 "the type and name should not be empty"); 1976 return; 1977 } 1978 Account resultAccount = new Account(name, type); 1979 if (!customTokens) { 1980 saveAuthTokenToDatabase( 1981 mAccounts, 1982 resultAccount, 1983 authTokenType, 1984 authToken); 1985 } 1986 long expiryMillis = result.getLong( 1987 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L); 1988 if (customTokens 1989 && expiryMillis > System.currentTimeMillis()) { 1990 saveCachedToken( 1991 mAccounts, 1992 account, 1993 callerPkg, 1994 callerPkgSigDigest, 1995 authTokenType, 1996 authToken, 1997 expiryMillis); 1998 } 1999 } 2000 2001 Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 2002 if (intent != null && notifyOnAuthFailure && !customTokens) { 2003 doNotification(mAccounts, 2004 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), 2005 intent, accounts.userId); 2006 } 2007 } 2008 super.onResult(result); 2009 } 2010 }.bind(); 2011 } finally { 2012 restoreCallingIdentity(identityToken); 2013 } 2014 } 2015 2016 private byte[] calculatePackageSignatureDigest(String callerPkg) { 2017 MessageDigest digester; 2018 try { 2019 digester = MessageDigest.getInstance("SHA-256"); 2020 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 2021 callerPkg, PackageManager.GET_SIGNATURES); 2022 for (Signature sig : pkgInfo.signatures) { 2023 digester.update(sig.toByteArray()); 2024 } 2025 } catch (NoSuchAlgorithmException x) { 2026 Log.wtf(TAG, "SHA-256 should be available", x); 2027 digester = null; 2028 } catch (NameNotFoundException e) { 2029 Log.w(TAG, "Could not find packageinfo for: " + callerPkg); 2030 digester = null; 2031 } 2032 return (digester == null) ? null : digester.digest(); 2033 } 2034 2035 private void createNoCredentialsPermissionNotification(Account account, Intent intent, 2036 int userId) { 2037 int uid = intent.getIntExtra( 2038 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); 2039 String authTokenType = intent.getStringExtra( 2040 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); 2041 final String titleAndSubtitle = 2042 mContext.getString(R.string.permission_request_notification_with_subtitle, 2043 account.name); 2044 final int index = titleAndSubtitle.indexOf('\n'); 2045 String title = titleAndSubtitle; 2046 String subtitle = ""; 2047 if (index > 0) { 2048 title = titleAndSubtitle.substring(0, index); 2049 subtitle = titleAndSubtitle.substring(index + 1); 2050 } 2051 UserHandle user = new UserHandle(userId); 2052 Context contextForUser = getContextForUser(user); 2053 Notification n = new Notification.Builder(contextForUser) 2054 .setSmallIcon(android.R.drawable.stat_sys_warning) 2055 .setWhen(0) 2056 .setColor(contextForUser.getColor( 2057 com.android.internal.R.color.system_notification_accent_color)) 2058 .setContentTitle(title) 2059 .setContentText(subtitle) 2060 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent, 2061 PendingIntent.FLAG_CANCEL_CURRENT, null, user)) 2062 .build(); 2063 installNotification(getCredentialPermissionNotificationId( 2064 account, authTokenType, uid), n, user); 2065 } 2066 2067 private Intent newGrantCredentialsPermissionIntent(Account account, int uid, 2068 AccountAuthenticatorResponse response, String authTokenType) { 2069 2070 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); 2071 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. 2072 // Since it was set in Eclair+ we can't change it without breaking apps using 2073 // the intent from a non-Activity context. 2074 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2075 intent.addCategory( 2076 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); 2077 2078 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); 2079 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); 2080 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); 2081 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); 2082 2083 return intent; 2084 } 2085 2086 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, 2087 int uid) { 2088 Integer id; 2089 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 2090 synchronized (accounts.credentialsPermissionNotificationIds) { 2091 final Pair<Pair<Account, String>, Integer> key = 2092 new Pair<Pair<Account, String>, Integer>( 2093 new Pair<Account, String>(account, authTokenType), uid); 2094 id = accounts.credentialsPermissionNotificationIds.get(key); 2095 if (id == null) { 2096 id = mNotificationIds.incrementAndGet(); 2097 accounts.credentialsPermissionNotificationIds.put(key, id); 2098 } 2099 } 2100 return id; 2101 } 2102 2103 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { 2104 Integer id; 2105 synchronized (accounts.signinRequiredNotificationIds) { 2106 id = accounts.signinRequiredNotificationIds.get(account); 2107 if (id == null) { 2108 id = mNotificationIds.incrementAndGet(); 2109 accounts.signinRequiredNotificationIds.put(account, id); 2110 } 2111 } 2112 return id; 2113 } 2114 2115 @Override 2116 public void addAccount(final IAccountManagerResponse response, final String accountType, 2117 final String authTokenType, final String[] requiredFeatures, 2118 final boolean expectActivityLaunch, final Bundle optionsIn) { 2119 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2120 Log.v(TAG, "addAccount: accountType " + accountType 2121 + ", response " + response 2122 + ", authTokenType " + authTokenType 2123 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2124 + ", expectActivityLaunch " + expectActivityLaunch 2125 + ", caller's uid " + Binder.getCallingUid() 2126 + ", pid " + Binder.getCallingPid()); 2127 } 2128 if (response == null) throw new IllegalArgumentException("response is null"); 2129 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2130 2131 // Is user disallowed from modifying accounts? 2132 int userId = Binder.getCallingUserHandle().getIdentifier(); 2133 if (!canUserModifyAccounts(userId)) { 2134 try { 2135 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2136 "User is not allowed to add an account!"); 2137 } catch (RemoteException re) { 2138 } 2139 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2140 return; 2141 } 2142 if (!canUserModifyAccountsForType(userId, accountType)) { 2143 try { 2144 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2145 "User cannot modify accounts of this type (policy)."); 2146 } catch (RemoteException re) { 2147 } 2148 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2149 userId); 2150 return; 2151 } 2152 2153 final int pid = Binder.getCallingPid(); 2154 final int uid = Binder.getCallingUid(); 2155 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2156 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2157 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2158 2159 int usrId = UserHandle.getCallingUserId(); 2160 long identityToken = clearCallingIdentity(); 2161 try { 2162 UserAccounts accounts = getUserAccounts(usrId); 2163 logRecordWithUid( 2164 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid); 2165 new Session(accounts, response, accountType, expectActivityLaunch, 2166 true /* stripAuthTokenFromResult */, null /* accountName */, 2167 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2168 @Override 2169 public void run() throws RemoteException { 2170 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2171 options); 2172 } 2173 2174 @Override 2175 protected String toDebugString(long now) { 2176 return super.toDebugString(now) + ", addAccount" 2177 + ", accountType " + accountType 2178 + ", requiredFeatures " 2179 + (requiredFeatures != null 2180 ? TextUtils.join(",", requiredFeatures) 2181 : null); 2182 } 2183 }.bind(); 2184 } finally { 2185 restoreCallingIdentity(identityToken); 2186 } 2187 } 2188 2189 @Override 2190 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 2191 final String authTokenType, final String[] requiredFeatures, 2192 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 2193 int callingUid = Binder.getCallingUid(); 2194 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2195 Log.v(TAG, "addAccount: accountType " + accountType 2196 + ", response " + response 2197 + ", authTokenType " + authTokenType 2198 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 2199 + ", expectActivityLaunch " + expectActivityLaunch 2200 + ", caller's uid " + Binder.getCallingUid() 2201 + ", pid " + Binder.getCallingPid() 2202 + ", for user id " + userId); 2203 } 2204 if (response == null) throw new IllegalArgumentException("response is null"); 2205 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2206 // Only allow the system process to add accounts of other users 2207 if (isCrossUser(callingUid, userId)) { 2208 throw new SecurityException( 2209 String.format( 2210 "User %s trying to add account for %s" , 2211 UserHandle.getCallingUserId(), 2212 userId)); 2213 } 2214 2215 // Is user disallowed from modifying accounts? 2216 if (!canUserModifyAccounts(userId)) { 2217 try { 2218 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 2219 "User is not allowed to add an account!"); 2220 } catch (RemoteException re) { 2221 } 2222 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId); 2223 return; 2224 } 2225 if (!canUserModifyAccountsForType(userId, accountType)) { 2226 try { 2227 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2228 "User cannot modify accounts of this type (policy)."); 2229 } catch (RemoteException re) { 2230 } 2231 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 2232 userId); 2233 return; 2234 } 2235 2236 final int pid = Binder.getCallingPid(); 2237 final int uid = Binder.getCallingUid(); 2238 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 2239 options.putInt(AccountManager.KEY_CALLER_UID, uid); 2240 options.putInt(AccountManager.KEY_CALLER_PID, pid); 2241 2242 long identityToken = clearCallingIdentity(); 2243 try { 2244 UserAccounts accounts = getUserAccounts(userId); 2245 logRecordWithUid( 2246 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId); 2247 new Session(accounts, response, accountType, expectActivityLaunch, 2248 true /* stripAuthTokenFromResult */, null /* accountName */, 2249 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { 2250 @Override 2251 public void run() throws RemoteException { 2252 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 2253 options); 2254 } 2255 2256 @Override 2257 protected String toDebugString(long now) { 2258 return super.toDebugString(now) + ", addAccount" 2259 + ", accountType " + accountType 2260 + ", requiredFeatures " 2261 + (requiredFeatures != null 2262 ? TextUtils.join(",", requiredFeatures) 2263 : null); 2264 } 2265 }.bind(); 2266 } finally { 2267 restoreCallingIdentity(identityToken); 2268 } 2269 } 2270 2271 private void showCantAddAccount(int errorCode, int userId) { 2272 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 2273 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 2274 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2275 long identityToken = clearCallingIdentity(); 2276 try { 2277 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId)); 2278 } finally { 2279 restoreCallingIdentity(identityToken); 2280 } 2281 } 2282 2283 @Override 2284 public void confirmCredentialsAsUser( 2285 IAccountManagerResponse response, 2286 final Account account, 2287 final Bundle options, 2288 final boolean expectActivityLaunch, 2289 int userId) { 2290 int callingUid = Binder.getCallingUid(); 2291 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2292 Log.v(TAG, "confirmCredentials: " + account 2293 + ", response " + response 2294 + ", expectActivityLaunch " + expectActivityLaunch 2295 + ", caller's uid " + callingUid 2296 + ", pid " + Binder.getCallingPid()); 2297 } 2298 // Only allow the system process to read accounts of other users 2299 if (isCrossUser(callingUid, userId)) { 2300 throw new SecurityException( 2301 String.format( 2302 "User %s trying to confirm account credentials for %s" , 2303 UserHandle.getCallingUserId(), 2304 userId)); 2305 } 2306 if (response == null) throw new IllegalArgumentException("response is null"); 2307 if (account == null) throw new IllegalArgumentException("account is null"); 2308 long identityToken = clearCallingIdentity(); 2309 try { 2310 UserAccounts accounts = getUserAccounts(userId); 2311 new Session(accounts, response, account.type, expectActivityLaunch, 2312 true /* stripAuthTokenFromResult */, account.name, 2313 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) { 2314 @Override 2315 public void run() throws RemoteException { 2316 mAuthenticator.confirmCredentials(this, account, options); 2317 } 2318 @Override 2319 protected String toDebugString(long now) { 2320 return super.toDebugString(now) + ", confirmCredentials" 2321 + ", " + account; 2322 } 2323 }.bind(); 2324 } finally { 2325 restoreCallingIdentity(identityToken); 2326 } 2327 } 2328 2329 @Override 2330 public void updateCredentials(IAccountManagerResponse response, final Account account, 2331 final String authTokenType, final boolean expectActivityLaunch, 2332 final Bundle loginOptions) { 2333 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2334 Log.v(TAG, "updateCredentials: " + account 2335 + ", response " + response 2336 + ", authTokenType " + authTokenType 2337 + ", expectActivityLaunch " + expectActivityLaunch 2338 + ", caller's uid " + Binder.getCallingUid() 2339 + ", pid " + Binder.getCallingPid()); 2340 } 2341 if (response == null) throw new IllegalArgumentException("response is null"); 2342 if (account == null) throw new IllegalArgumentException("account is null"); 2343 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2344 int userId = UserHandle.getCallingUserId(); 2345 long identityToken = clearCallingIdentity(); 2346 try { 2347 UserAccounts accounts = getUserAccounts(userId); 2348 new Session(accounts, response, account.type, expectActivityLaunch, 2349 true /* stripAuthTokenFromResult */, account.name, 2350 false /* authDetailsRequired */, true /* updateLastCredentialTime */) { 2351 @Override 2352 public void run() throws RemoteException { 2353 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 2354 } 2355 @Override 2356 protected String toDebugString(long now) { 2357 if (loginOptions != null) loginOptions.keySet(); 2358 return super.toDebugString(now) + ", updateCredentials" 2359 + ", " + account 2360 + ", authTokenType " + authTokenType 2361 + ", loginOptions " + loginOptions; 2362 } 2363 }.bind(); 2364 } finally { 2365 restoreCallingIdentity(identityToken); 2366 } 2367 } 2368 2369 @Override 2370 public void editProperties(IAccountManagerResponse response, final String accountType, 2371 final boolean expectActivityLaunch) { 2372 final int callingUid = Binder.getCallingUid(); 2373 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2374 Log.v(TAG, "editProperties: accountType " + accountType 2375 + ", response " + response 2376 + ", expectActivityLaunch " + expectActivityLaunch 2377 + ", caller's uid " + callingUid 2378 + ", pid " + Binder.getCallingPid()); 2379 } 2380 if (response == null) throw new IllegalArgumentException("response is null"); 2381 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2382 int userId = UserHandle.getCallingUserId(); 2383 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { 2384 String msg = String.format( 2385 "uid %s cannot edit authenticator properites for account type: %s", 2386 callingUid, 2387 accountType); 2388 throw new SecurityException(msg); 2389 } 2390 long identityToken = clearCallingIdentity(); 2391 try { 2392 UserAccounts accounts = getUserAccounts(userId); 2393 new Session(accounts, response, accountType, expectActivityLaunch, 2394 true /* stripAuthTokenFromResult */, null /* accountName */, 2395 false /* authDetailsRequired */) { 2396 @Override 2397 public void run() throws RemoteException { 2398 mAuthenticator.editProperties(this, mAccountType); 2399 } 2400 @Override 2401 protected String toDebugString(long now) { 2402 return super.toDebugString(now) + ", editProperties" 2403 + ", accountType " + accountType; 2404 } 2405 }.bind(); 2406 } finally { 2407 restoreCallingIdentity(identityToken); 2408 } 2409 } 2410 2411 private class GetAccountsByTypeAndFeatureSession extends Session { 2412 private final String[] mFeatures; 2413 private volatile Account[] mAccountsOfType = null; 2414 private volatile ArrayList<Account> mAccountsWithFeatures = null; 2415 private volatile int mCurrentAccount = 0; 2416 private final int mCallingUid; 2417 2418 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 2419 IAccountManagerResponse response, String type, String[] features, int callingUid) { 2420 super(accounts, response, type, false /* expectActivityLaunch */, 2421 true /* stripAuthTokenFromResult */, null /* accountName */, 2422 false /* authDetailsRequired */); 2423 mCallingUid = callingUid; 2424 mFeatures = features; 2425 } 2426 2427 @Override 2428 public void run() throws RemoteException { 2429 synchronized (mAccounts.cacheLock) { 2430 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 2431 null); 2432 } 2433 // check whether each account matches the requested features 2434 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 2435 mCurrentAccount = 0; 2436 2437 checkAccount(); 2438 } 2439 2440 public void checkAccount() { 2441 if (mCurrentAccount >= mAccountsOfType.length) { 2442 sendResult(); 2443 return; 2444 } 2445 2446 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 2447 if (accountAuthenticator == null) { 2448 // It is possible that the authenticator has died, which is indicated by 2449 // mAuthenticator being set to null. If this happens then just abort. 2450 // There is no need to send back a result or error in this case since 2451 // that already happened when mAuthenticator was cleared. 2452 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2453 Log.v(TAG, "checkAccount: aborting session since we are no longer" 2454 + " connected to the authenticator, " + toDebugString()); 2455 } 2456 return; 2457 } 2458 try { 2459 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 2460 } catch (RemoteException e) { 2461 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 2462 } 2463 } 2464 2465 @Override 2466 public void onResult(Bundle result) { 2467 mNumResults++; 2468 if (result == null) { 2469 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 2470 return; 2471 } 2472 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 2473 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 2474 } 2475 mCurrentAccount++; 2476 checkAccount(); 2477 } 2478 2479 public void sendResult() { 2480 IAccountManagerResponse response = getResponseAndClose(); 2481 if (response != null) { 2482 try { 2483 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 2484 for (int i = 0; i < accounts.length; i++) { 2485 accounts[i] = mAccountsWithFeatures.get(i); 2486 } 2487 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2488 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2489 + response); 2490 } 2491 Bundle result = new Bundle(); 2492 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2493 response.onResult(result); 2494 } catch (RemoteException e) { 2495 // if the caller is dead then there is no one to care about remote exceptions 2496 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2497 Log.v(TAG, "failure while notifying response", e); 2498 } 2499 } 2500 } 2501 } 2502 2503 2504 @Override 2505 protected String toDebugString(long now) { 2506 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 2507 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 2508 } 2509 } 2510 2511 /** 2512 * Returns the accounts visible to the client within the context of a specific user 2513 * @hide 2514 */ 2515 public Account[] getAccounts(int userId, String opPackageName) { 2516 int callingUid = Binder.getCallingUid(); 2517 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 2518 opPackageName); 2519 if (visibleAccountTypes.isEmpty()) { 2520 return new Account[0]; 2521 } 2522 long identityToken = clearCallingIdentity(); 2523 try { 2524 UserAccounts accounts = getUserAccounts(userId); 2525 return getAccountsInternal( 2526 accounts, 2527 callingUid, 2528 null, // packageName 2529 visibleAccountTypes); 2530 } finally { 2531 restoreCallingIdentity(identityToken); 2532 } 2533 } 2534 2535 /** 2536 * Returns accounts for all running users. 2537 * 2538 * @hide 2539 */ 2540 public AccountAndUser[] getRunningAccounts() { 2541 final int[] runningUserIds; 2542 try { 2543 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 2544 } catch (RemoteException e) { 2545 // Running in system_server; should never happen 2546 throw new RuntimeException(e); 2547 } 2548 return getAccounts(runningUserIds); 2549 } 2550 2551 /** {@hide} */ 2552 public AccountAndUser[] getAllAccounts() { 2553 final List<UserInfo> users = getUserManager().getUsers(); 2554 final int[] userIds = new int[users.size()]; 2555 for (int i = 0; i < userIds.length; i++) { 2556 userIds[i] = users.get(i).id; 2557 } 2558 return getAccounts(userIds); 2559 } 2560 2561 private AccountAndUser[] getAccounts(int[] userIds) { 2562 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 2563 for (int userId : userIds) { 2564 UserAccounts userAccounts = getUserAccounts(userId); 2565 if (userAccounts == null) continue; 2566 synchronized (userAccounts.cacheLock) { 2567 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 2568 Binder.getCallingUid(), null); 2569 for (int a = 0; a < accounts.length; a++) { 2570 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 2571 } 2572 } 2573 } 2574 2575 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 2576 return runningAccounts.toArray(accountsArray); 2577 } 2578 2579 @Override 2580 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { 2581 return getAccountsAsUser(type, userId, null, -1, opPackageName); 2582 } 2583 2584 private Account[] getAccountsAsUser( 2585 String type, 2586 int userId, 2587 String callingPackage, 2588 int packageUid, 2589 String opPackageName) { 2590 int callingUid = Binder.getCallingUid(); 2591 // Only allow the system process to read accounts of other users 2592 if (userId != UserHandle.getCallingUserId() 2593 && callingUid != Process.myUid() 2594 && mContext.checkCallingOrSelfPermission( 2595 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 2596 != PackageManager.PERMISSION_GRANTED) { 2597 throw new SecurityException("User " + UserHandle.getCallingUserId() 2598 + " trying to get account for " + userId); 2599 } 2600 2601 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2602 Log.v(TAG, "getAccounts: accountType " + type 2603 + ", caller's uid " + Binder.getCallingUid() 2604 + ", pid " + Binder.getCallingPid()); 2605 } 2606 // If the original calling app was using the framework account chooser activity, we'll 2607 // be passed in the original caller's uid here, which is what should be used for filtering. 2608 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 2609 callingUid = packageUid; 2610 opPackageName = callingPackage; 2611 } 2612 2613 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 2614 opPackageName); 2615 if (visibleAccountTypes.isEmpty() 2616 || (type != null && !visibleAccountTypes.contains(type))) { 2617 return new Account[0]; 2618 } else if (visibleAccountTypes.contains(type)) { 2619 // Prune the list down to just the requested type. 2620 visibleAccountTypes = new ArrayList<>(); 2621 visibleAccountTypes.add(type); 2622 } // else aggregate all the visible accounts (it won't matter if the 2623 // list is empty). 2624 2625 long identityToken = clearCallingIdentity(); 2626 try { 2627 UserAccounts accounts = getUserAccounts(userId); 2628 return getAccountsInternal( 2629 accounts, 2630 callingUid, 2631 callingPackage, 2632 visibleAccountTypes); 2633 } finally { 2634 restoreCallingIdentity(identityToken); 2635 } 2636 } 2637 2638 private Account[] getAccountsInternal( 2639 UserAccounts userAccounts, 2640 int callingUid, 2641 String callingPackage, 2642 List<String> visibleAccountTypes) { 2643 synchronized (userAccounts.cacheLock) { 2644 ArrayList<Account> visibleAccounts = new ArrayList<>(); 2645 for (String visibleType : visibleAccountTypes) { 2646 Account[] accountsForType = getAccountsFromCacheLocked( 2647 userAccounts, visibleType, callingUid, callingPackage); 2648 if (accountsForType != null) { 2649 visibleAccounts.addAll(Arrays.asList(accountsForType)); 2650 } 2651 } 2652 Account[] result = new Account[visibleAccounts.size()]; 2653 for (int i = 0; i < visibleAccounts.size(); i++) { 2654 result[i] = visibleAccounts.get(i); 2655 } 2656 return result; 2657 } 2658 } 2659 2660 @Override 2661 public boolean addSharedAccountAsUser(Account account, int userId) { 2662 userId = handleIncomingUser(userId); 2663 UserAccounts accounts = getUserAccounts(userId); 2664 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2665 ContentValues values = new ContentValues(); 2666 values.put(ACCOUNTS_NAME, account.name); 2667 values.put(ACCOUNTS_TYPE, account.type); 2668 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2669 new String[] {account.name, account.type}); 2670 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 2671 if (accountId < 0) { 2672 Log.w(TAG, "insertAccountIntoDatabase: " + account 2673 + ", skipping the DB insert failed"); 2674 return false; 2675 } 2676 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts); 2677 return true; 2678 } 2679 2680 @Override 2681 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 2682 userId = handleIncomingUser(userId); 2683 UserAccounts accounts = getUserAccounts(userId); 2684 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2685 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 2686 final ContentValues values = new ContentValues(); 2687 values.put(ACCOUNTS_NAME, newName); 2688 values.put(ACCOUNTS_PREVIOUS_NAME, account.name); 2689 int r = db.update( 2690 TABLE_SHARED_ACCOUNTS, 2691 values, 2692 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2693 new String[] { account.name, account.type }); 2694 if (r > 0) { 2695 int callingUid = getCallingUid(); 2696 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS, 2697 sharedTableAccountId, accounts, callingUid); 2698 // Recursively rename the account. 2699 renameAccountInternal(accounts, account, newName); 2700 } 2701 return r > 0; 2702 } 2703 2704 @Override 2705 public boolean removeSharedAccountAsUser(Account account, int userId) { 2706 return removeSharedAccountAsUser(account, userId, getCallingUid()); 2707 } 2708 2709 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) { 2710 userId = handleIncomingUser(userId); 2711 UserAccounts accounts = getUserAccounts(userId); 2712 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2713 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 2714 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2715 new String[] {account.name, account.type}); 2716 if (r > 0) { 2717 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS, 2718 sharedTableAccountId, accounts, callingUid); 2719 removeAccountInternal(accounts, account); 2720 } 2721 return r > 0; 2722 } 2723 2724 @Override 2725 public Account[] getSharedAccountsAsUser(int userId) { 2726 userId = handleIncomingUser(userId); 2727 UserAccounts accounts = getUserAccounts(userId); 2728 ArrayList<Account> accountList = new ArrayList<Account>(); 2729 Cursor cursor = null; 2730 try { 2731 cursor = accounts.openHelper.getReadableDatabase() 2732 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 2733 null, null, null, null, null); 2734 if (cursor != null && cursor.moveToFirst()) { 2735 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 2736 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 2737 do { 2738 accountList.add(new Account(cursor.getString(nameIndex), 2739 cursor.getString(typeIndex))); 2740 } while (cursor.moveToNext()); 2741 } 2742 } finally { 2743 if (cursor != null) { 2744 cursor.close(); 2745 } 2746 } 2747 Account[] accountArray = new Account[accountList.size()]; 2748 accountList.toArray(accountArray); 2749 return accountArray; 2750 } 2751 2752 @Override 2753 public Account[] getAccounts(String type, String opPackageName) { 2754 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName); 2755 } 2756 2757 @Override 2758 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { 2759 int callingUid = Binder.getCallingUid(); 2760 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 2761 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 2762 + callingUid + " with uid=" + uid); 2763 } 2764 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid, 2765 opPackageName); 2766 } 2767 2768 @Override 2769 public Account[] getAccountsByTypeForPackage(String type, String packageName, 2770 String opPackageName) { 2771 int packageUid = -1; 2772 try { 2773 packageUid = AppGlobals.getPackageManager().getPackageUid( 2774 packageName, UserHandle.getCallingUserId()); 2775 } catch (RemoteException re) { 2776 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 2777 return new Account[0]; 2778 } 2779 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, 2780 packageUid, opPackageName); 2781 } 2782 2783 @Override 2784 public void getAccountsByFeatures( 2785 IAccountManagerResponse response, 2786 String type, 2787 String[] features, 2788 String opPackageName) { 2789 int callingUid = Binder.getCallingUid(); 2790 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2791 Log.v(TAG, "getAccounts: accountType " + type 2792 + ", response " + response 2793 + ", features " + stringArrayToString(features) 2794 + ", caller's uid " + callingUid 2795 + ", pid " + Binder.getCallingPid()); 2796 } 2797 if (response == null) throw new IllegalArgumentException("response is null"); 2798 if (type == null) throw new IllegalArgumentException("accountType is null"); 2799 int userId = UserHandle.getCallingUserId(); 2800 2801 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 2802 opPackageName); 2803 if (!visibleAccountTypes.contains(type)) { 2804 Bundle result = new Bundle(); 2805 // Need to return just the accounts that are from matching signatures. 2806 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); 2807 try { 2808 response.onResult(result); 2809 } catch (RemoteException e) { 2810 Log.e(TAG, "Cannot respond to caller do to exception." , e); 2811 } 2812 return; 2813 } 2814 long identityToken = clearCallingIdentity(); 2815 try { 2816 UserAccounts userAccounts = getUserAccounts(userId); 2817 if (features == null || features.length == 0) { 2818 Account[] accounts; 2819 synchronized (userAccounts.cacheLock) { 2820 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 2821 } 2822 Bundle result = new Bundle(); 2823 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2824 onResult(response, result); 2825 return; 2826 } 2827 new GetAccountsByTypeAndFeatureSession( 2828 userAccounts, 2829 response, 2830 type, 2831 features, 2832 callingUid).bind(); 2833 } finally { 2834 restoreCallingIdentity(identityToken); 2835 } 2836 } 2837 2838 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) { 2839 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID}, 2840 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 2841 try { 2842 if (cursor.moveToNext()) { 2843 return cursor.getLong(0); 2844 } 2845 return -1; 2846 } finally { 2847 cursor.close(); 2848 } 2849 } 2850 2851 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 2852 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 2853 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 2854 try { 2855 if (cursor.moveToNext()) { 2856 return cursor.getLong(0); 2857 } 2858 return -1; 2859 } finally { 2860 cursor.close(); 2861 } 2862 } 2863 2864 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 2865 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, 2866 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 2867 new String[]{key}, null, null, null); 2868 try { 2869 if (cursor.moveToNext()) { 2870 return cursor.getLong(0); 2871 } 2872 return -1; 2873 } finally { 2874 cursor.close(); 2875 } 2876 } 2877 2878 private abstract class Session extends IAccountAuthenticatorResponse.Stub 2879 implements IBinder.DeathRecipient, ServiceConnection { 2880 IAccountManagerResponse mResponse; 2881 final String mAccountType; 2882 final boolean mExpectActivityLaunch; 2883 final long mCreationTime; 2884 final String mAccountName; 2885 // Indicates if we need to add auth details(like last credential time) 2886 final boolean mAuthDetailsRequired; 2887 // If set, we need to update the last authenticated time. This is 2888 // currently 2889 // used on 2890 // successful confirming credentials. 2891 final boolean mUpdateLastAuthenticatedTime; 2892 2893 public int mNumResults = 0; 2894 private int mNumRequestContinued = 0; 2895 private int mNumErrors = 0; 2896 2897 IAccountAuthenticator mAuthenticator = null; 2898 2899 private final boolean mStripAuthTokenFromResult; 2900 protected final UserAccounts mAccounts; 2901 2902 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 2903 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 2904 boolean authDetailsRequired) { 2905 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult, 2906 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */); 2907 } 2908 2909 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 2910 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 2911 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) { 2912 super(); 2913 //if (response == null) throw new IllegalArgumentException("response is null"); 2914 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2915 mAccounts = accounts; 2916 mStripAuthTokenFromResult = stripAuthTokenFromResult; 2917 mResponse = response; 2918 mAccountType = accountType; 2919 mExpectActivityLaunch = expectActivityLaunch; 2920 mCreationTime = SystemClock.elapsedRealtime(); 2921 mAccountName = accountName; 2922 mAuthDetailsRequired = authDetailsRequired; 2923 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; 2924 2925 synchronized (mSessions) { 2926 mSessions.put(toString(), this); 2927 } 2928 if (response != null) { 2929 try { 2930 response.asBinder().linkToDeath(this, 0 /* flags */); 2931 } catch (RemoteException e) { 2932 mResponse = null; 2933 binderDied(); 2934 } 2935 } 2936 } 2937 2938 IAccountManagerResponse getResponseAndClose() { 2939 if (mResponse == null) { 2940 // this session has already been closed 2941 return null; 2942 } 2943 IAccountManagerResponse response = mResponse; 2944 close(); // this clears mResponse so we need to save the response before this call 2945 return response; 2946 } 2947 2948 private void close() { 2949 synchronized (mSessions) { 2950 if (mSessions.remove(toString()) == null) { 2951 // the session was already closed, so bail out now 2952 return; 2953 } 2954 } 2955 if (mResponse != null) { 2956 // stop listening for response deaths 2957 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 2958 2959 // clear this so that we don't accidentally send any further results 2960 mResponse = null; 2961 } 2962 cancelTimeout(); 2963 unbind(); 2964 } 2965 2966 @Override 2967 public void binderDied() { 2968 mResponse = null; 2969 close(); 2970 } 2971 2972 protected String toDebugString() { 2973 return toDebugString(SystemClock.elapsedRealtime()); 2974 } 2975 2976 protected String toDebugString(long now) { 2977 return "Session: expectLaunch " + mExpectActivityLaunch 2978 + ", connected " + (mAuthenticator != null) 2979 + ", stats (" + mNumResults + "/" + mNumRequestContinued 2980 + "/" + mNumErrors + ")" 2981 + ", lifetime " + ((now - mCreationTime) / 1000.0); 2982 } 2983 2984 void bind() { 2985 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2986 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 2987 } 2988 if (!bindToAuthenticator(mAccountType)) { 2989 Log.d(TAG, "bind attempt failed for " + toDebugString()); 2990 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 2991 } 2992 } 2993 2994 private void unbind() { 2995 if (mAuthenticator != null) { 2996 mAuthenticator = null; 2997 mContext.unbindService(this); 2998 } 2999 } 3000 3001 public void cancelTimeout() { 3002 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 3003 } 3004 3005 @Override 3006 public void onServiceConnected(ComponentName name, IBinder service) { 3007 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 3008 try { 3009 run(); 3010 } catch (RemoteException e) { 3011 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3012 "remote exception"); 3013 } 3014 } 3015 3016 @Override 3017 public void onServiceDisconnected(ComponentName name) { 3018 mAuthenticator = null; 3019 IAccountManagerResponse response = getResponseAndClose(); 3020 if (response != null) { 3021 try { 3022 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3023 "disconnected"); 3024 } catch (RemoteException e) { 3025 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3026 Log.v(TAG, "Session.onServiceDisconnected: " 3027 + "caught RemoteException while responding", e); 3028 } 3029 } 3030 } 3031 } 3032 3033 public abstract void run() throws RemoteException; 3034 3035 public void onTimedOut() { 3036 IAccountManagerResponse response = getResponseAndClose(); 3037 if (response != null) { 3038 try { 3039 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3040 "timeout"); 3041 } catch (RemoteException e) { 3042 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3043 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 3044 e); 3045 } 3046 } 3047 } 3048 } 3049 3050 @Override 3051 public void onResult(Bundle result) { 3052 mNumResults++; 3053 Intent intent = null; 3054 if (result != null) { 3055 boolean isSuccessfulConfirmCreds = result.getBoolean( 3056 AccountManager.KEY_BOOLEAN_RESULT, false); 3057 boolean isSuccessfulUpdateCredsOrAddAccount = 3058 result.containsKey(AccountManager.KEY_ACCOUNT_NAME) 3059 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE); 3060 // We should only update lastAuthenticated time, if 3061 // mUpdateLastAuthenticatedTime is true and the confirmRequest 3062 // or updateRequest was successful 3063 boolean needUpdate = mUpdateLastAuthenticatedTime 3064 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount); 3065 if (needUpdate || mAuthDetailsRequired) { 3066 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType); 3067 if (needUpdate && accountPresent) { 3068 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType)); 3069 } 3070 if (mAuthDetailsRequired) { 3071 long lastAuthenticatedTime = -1; 3072 if (accountPresent) { 3073 lastAuthenticatedTime = DatabaseUtils.longForQuery( 3074 mAccounts.openHelper.getReadableDatabase(), 3075 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 3076 + " from " + 3077 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND " 3078 + ACCOUNTS_TYPE + "=?", 3079 new String[] { 3080 mAccountName, mAccountType 3081 }); 3082 } 3083 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME, 3084 lastAuthenticatedTime); 3085 } 3086 } 3087 } 3088 if (result != null 3089 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 3090 /* 3091 * The Authenticator API allows third party authenticators to 3092 * supply arbitrary intents to other apps that they can run, 3093 * this can be very bad when those apps are in the system like 3094 * the System Settings. 3095 */ 3096 int authenticatorUid = Binder.getCallingUid(); 3097 long bid = Binder.clearCallingIdentity(); 3098 try { 3099 PackageManager pm = mContext.getPackageManager(); 3100 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 3101 int targetUid = resolveInfo.activityInfo.applicationInfo.uid; 3102 if (PackageManager.SIGNATURE_MATCH != 3103 pm.checkSignatures(authenticatorUid, targetUid)) { 3104 throw new SecurityException( 3105 "Activity to be started with KEY_INTENT must " + 3106 "share Authenticator's signatures"); 3107 } 3108 } finally { 3109 Binder.restoreCallingIdentity(bid); 3110 } 3111 } 3112 if (result != null 3113 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 3114 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 3115 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 3116 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 3117 Account account = new Account(accountName, accountType); 3118 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 3119 new UserHandle(mAccounts.userId)); 3120 } 3121 } 3122 IAccountManagerResponse response; 3123 if (mExpectActivityLaunch && result != null 3124 && result.containsKey(AccountManager.KEY_INTENT)) { 3125 response = mResponse; 3126 } else { 3127 response = getResponseAndClose(); 3128 } 3129 if (response != null) { 3130 try { 3131 if (result == null) { 3132 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3133 Log.v(TAG, getClass().getSimpleName() 3134 + " calling onError() on response " + response); 3135 } 3136 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 3137 "null bundle returned"); 3138 } else { 3139 if (mStripAuthTokenFromResult) { 3140 result.remove(AccountManager.KEY_AUTHTOKEN); 3141 } 3142 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3143 Log.v(TAG, getClass().getSimpleName() 3144 + " calling onResult() on response " + response); 3145 } 3146 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 3147 (intent == null)) { 3148 // All AccountManager error codes are greater than 0 3149 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 3150 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3151 } else { 3152 response.onResult(result); 3153 } 3154 } 3155 } catch (RemoteException e) { 3156 // if the caller is dead then there is no one to care about remote exceptions 3157 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3158 Log.v(TAG, "failure while notifying response", e); 3159 } 3160 } 3161 } 3162 } 3163 3164 @Override 3165 public void onRequestContinued() { 3166 mNumRequestContinued++; 3167 } 3168 3169 @Override 3170 public void onError(int errorCode, String errorMessage) { 3171 mNumErrors++; 3172 IAccountManagerResponse response = getResponseAndClose(); 3173 if (response != null) { 3174 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3175 Log.v(TAG, getClass().getSimpleName() 3176 + " calling onError() on response " + response); 3177 } 3178 try { 3179 response.onError(errorCode, errorMessage); 3180 } catch (RemoteException e) { 3181 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3182 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 3183 } 3184 } 3185 } else { 3186 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3187 Log.v(TAG, "Session.onError: already closed"); 3188 } 3189 } 3190 } 3191 3192 /** 3193 * find the component name for the authenticator and initiate a bind 3194 * if no authenticator or the bind fails then return false, otherwise return true 3195 */ 3196 private boolean bindToAuthenticator(String authenticatorType) { 3197 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 3198 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 3199 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 3200 if (authenticatorInfo == null) { 3201 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3202 Log.v(TAG, "there is no authenticator for " + authenticatorType 3203 + ", bailing out"); 3204 } 3205 return false; 3206 } 3207 3208 Intent intent = new Intent(); 3209 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 3210 intent.setComponent(authenticatorInfo.componentName); 3211 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3212 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 3213 } 3214 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 3215 new UserHandle(mAccounts.userId))) { 3216 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3217 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 3218 } 3219 return false; 3220 } 3221 3222 3223 return true; 3224 } 3225 } 3226 3227 private class MessageHandler extends Handler { 3228 MessageHandler(Looper looper) { 3229 super(looper); 3230 } 3231 3232 @Override 3233 public void handleMessage(Message msg) { 3234 switch (msg.what) { 3235 case MESSAGE_TIMED_OUT: 3236 Session session = (Session)msg.obj; 3237 session.onTimedOut(); 3238 break; 3239 3240 case MESSAGE_COPY_SHARED_ACCOUNT: 3241 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); 3242 break; 3243 3244 default: 3245 throw new IllegalStateException("unhandled message: " + msg.what); 3246 } 3247 } 3248 } 3249 3250 private static String getDatabaseName(int userId) { 3251 File systemDir = Environment.getSystemSecureDirectory(); 3252 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); 3253 if (userId == 0) { 3254 // Migrate old file, if it exists, to the new location. 3255 // Make sure the new file doesn't already exist. A dummy file could have been 3256 // accidentally created in the old location, causing the new one to become corrupted 3257 // as well. 3258 File oldFile = new File(systemDir, DATABASE_NAME); 3259 if (oldFile.exists() && !databaseFile.exists()) { 3260 // Check for use directory; create if it doesn't exist, else renameTo will fail 3261 File userDir = Environment.getUserSystemDirectory(userId); 3262 if (!userDir.exists()) { 3263 if (!userDir.mkdirs()) { 3264 throw new IllegalStateException("User dir cannot be created: " + userDir); 3265 } 3266 } 3267 if (!oldFile.renameTo(databaseFile)) { 3268 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 3269 } 3270 } 3271 } 3272 return databaseFile.getPath(); 3273 } 3274 3275 private static class DebugDbHelper{ 3276 private DebugDbHelper() { 3277 } 3278 3279 private static String TABLE_DEBUG = "debug_table"; 3280 3281 // Columns for the table 3282 private static String ACTION_TYPE = "action_type"; 3283 private static String TIMESTAMP = "time"; 3284 private static String CALLER_UID = "caller_uid"; 3285 private static String TABLE_NAME = "table_name"; 3286 private static String KEY = "primary_key"; 3287 3288 // These actions correspond to the occurrence of real actions. Since 3289 // these are called by the authenticators, the uid associated will be 3290 // of the authenticator. 3291 private static String ACTION_SET_PASSWORD = "action_set_password"; 3292 private static String ACTION_CLEAR_PASSWORD = "action_clear_password"; 3293 private static String ACTION_ACCOUNT_ADD = "action_account_add"; 3294 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove"; 3295 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove"; 3296 private static String ACTION_ACCOUNT_RENAME = "action_account_rename"; 3297 3298 // These actions don't necessarily correspond to any action on 3299 // accountDb taking place. As an example, there might be a request for 3300 // addingAccount, which might not lead to addition of account on grounds 3301 // of bad authentication. We will still be logging it to keep track of 3302 // who called. 3303 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add"; 3304 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove"; 3305 3306 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 3307 3308 private static void createDebugTable(SQLiteDatabase db) { 3309 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " 3310 + ACCOUNTS_ID + " INTEGER," 3311 + ACTION_TYPE + " TEXT NOT NULL, " 3312 + TIMESTAMP + " DATETIME," 3313 + CALLER_UID + " INTEGER NOT NULL," 3314 + TABLE_NAME + " TEXT NOT NULL," 3315 + KEY + " INTEGER PRIMARY KEY)"); 3316 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")"); 3317 } 3318 } 3319 3320 private void logRecord(UserAccounts accounts, String action, String tableName) { 3321 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3322 logRecord(db, action, tableName, -1, accounts); 3323 } 3324 3325 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) { 3326 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3327 logRecord(db, action, tableName, -1, accounts, uid); 3328 } 3329 3330 /* 3331 * This function receives an opened writable database. 3332 */ 3333 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 3334 UserAccounts userAccount) { 3335 logRecord(db, action, tableName, accountId, userAccount, getCallingUid()); 3336 } 3337 3338 /* 3339 * This function receives an opened writable database. 3340 */ 3341 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 3342 UserAccounts userAccount, int callingUid) { 3343 SQLiteStatement logStatement = userAccount.statementForLogging; 3344 logStatement.bindLong(1, accountId); 3345 logStatement.bindString(2, action); 3346 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date())); 3347 logStatement.bindLong(4, callingUid); 3348 logStatement.bindString(5, tableName); 3349 logStatement.bindLong(6, userAccount.debugDbInsertionPoint); 3350 logStatement.execute(); 3351 logStatement.clearBindings(); 3352 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1) 3353 % MAX_DEBUG_DB_SIZE; 3354 } 3355 3356 /* 3357 * This should only be called once to compile the sql statement for logging 3358 * and to find the insertion point. 3359 */ 3360 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db, 3361 UserAccounts userAccount) { 3362 // Initialize the count if not done earlier. 3363 int size = (int) getDebugTableRowCount(db); 3364 if (size >= MAX_DEBUG_DB_SIZE) { 3365 // Table is full, and we need to find the point where to insert. 3366 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db); 3367 } else { 3368 userAccount.debugDbInsertionPoint = size; 3369 } 3370 compileSqlStatementForLogging(db, userAccount); 3371 } 3372 3373 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) { 3374 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG 3375 + " VALUES (?,?,?,?,?,?)"; 3376 userAccount.statementForLogging = db.compileStatement(sql); 3377 } 3378 3379 private long getDebugTableRowCount(SQLiteDatabase db) { 3380 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG; 3381 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 3382 } 3383 3384 /* 3385 * Finds the row key where the next insertion should take place. This should 3386 * be invoked only if the table has reached its full capacity. 3387 */ 3388 private long getDebugTableInsertionPoint(SQLiteDatabase db) { 3389 // This query finds the smallest timestamp value (and if 2 records have 3390 // same timestamp, the choose the lower id). 3391 String queryCountDebugDbRows = new StringBuilder() 3392 .append("SELECT ").append(DebugDbHelper.KEY) 3393 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG) 3394 .append(" ORDER BY ") 3395 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY) 3396 .append(" LIMIT 1") 3397 .toString(); 3398 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 3399 } 3400 3401 static class DatabaseHelper extends SQLiteOpenHelper { 3402 3403 public DatabaseHelper(Context context, int userId) { 3404 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); 3405 } 3406 3407 /** 3408 * This call needs to be made while the mCacheLock is held. The way to 3409 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 3410 * @param db The database. 3411 */ 3412 @Override 3413 public void onCreate(SQLiteDatabase db) { 3414 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 3415 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3416 + ACCOUNTS_NAME + " TEXT NOT NULL, " 3417 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 3418 + ACCOUNTS_PASSWORD + " TEXT, " 3419 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 3420 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, " 3421 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 3422 3423 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 3424 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3425 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 3426 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 3427 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 3428 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 3429 3430 createGrantsTable(db); 3431 3432 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 3433 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3434 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 3435 + EXTRAS_KEY + " TEXT NOT NULL, " 3436 + EXTRAS_VALUE + " TEXT, " 3437 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 3438 3439 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 3440 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 3441 + META_VALUE + " TEXT)"); 3442 3443 createSharedAccountsTable(db); 3444 3445 createAccountsDeletionTrigger(db); 3446 3447 DebugDbHelper.createDebugTable(db); 3448 } 3449 3450 private void createSharedAccountsTable(SQLiteDatabase db) { 3451 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 3452 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3453 + ACCOUNTS_NAME + " TEXT NOT NULL, " 3454 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 3455 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 3456 } 3457 3458 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) { 3459 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " 3460 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0"); 3461 } 3462 3463 private void addOldAccountNameColumn(SQLiteDatabase db) { 3464 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 3465 } 3466 3467 private void addDebugTable(SQLiteDatabase db) { 3468 DebugDbHelper.createDebugTable(db); 3469 } 3470 3471 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 3472 db.execSQL("" 3473 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 3474 + " BEGIN" 3475 + " DELETE FROM " + TABLE_AUTHTOKENS 3476 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3477 + " DELETE FROM " + TABLE_EXTRAS 3478 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3479 + " DELETE FROM " + TABLE_GRANTS 3480 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3481 + " END"); 3482 } 3483 3484 private void createGrantsTable(SQLiteDatabase db) { 3485 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 3486 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 3487 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 3488 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 3489 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 3490 + "," + GRANTS_GRANTEE_UID + "))"); 3491 } 3492 3493 @Override 3494 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 3495 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 3496 3497 if (oldVersion == 1) { 3498 // no longer need to do anything since the work is done 3499 // when upgrading from version 2 3500 oldVersion++; 3501 } 3502 3503 if (oldVersion == 2) { 3504 createGrantsTable(db); 3505 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 3506 createAccountsDeletionTrigger(db); 3507 oldVersion++; 3508 } 3509 3510 if (oldVersion == 3) { 3511 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 3512 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 3513 oldVersion++; 3514 } 3515 3516 if (oldVersion == 4) { 3517 createSharedAccountsTable(db); 3518 oldVersion++; 3519 } 3520 3521 if (oldVersion == 5) { 3522 addOldAccountNameColumn(db); 3523 oldVersion++; 3524 } 3525 3526 if (oldVersion == 6) { 3527 addLastSuccessfullAuthenticatedTimeColumn(db); 3528 oldVersion++; 3529 } 3530 3531 if (oldVersion == 7) { 3532 addDebugTable(db); 3533 oldVersion++; 3534 } 3535 3536 if (oldVersion != newVersion) { 3537 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 3538 } 3539 } 3540 3541 @Override 3542 public void onOpen(SQLiteDatabase db) { 3543 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 3544 } 3545 } 3546 3547 public IBinder onBind(@SuppressWarnings("unused") Intent intent) { 3548 return asBinder(); 3549 } 3550 3551 /** 3552 * Searches array of arguments for the specified string 3553 * @param args array of argument strings 3554 * @param value value to search for 3555 * @return true if the value is contained in the array 3556 */ 3557 private static boolean scanArgs(String[] args, String value) { 3558 if (args != null) { 3559 for (String arg : args) { 3560 if (value.equals(arg)) { 3561 return true; 3562 } 3563 } 3564 } 3565 return false; 3566 } 3567 3568 @Override 3569 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 3570 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 3571 != PackageManager.PERMISSION_GRANTED) { 3572 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 3573 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 3574 + " without permission " + android.Manifest.permission.DUMP); 3575 return; 3576 } 3577 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 3578 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 3579 3580 final List<UserInfo> users = getUserManager().getUsers(); 3581 for (UserInfo user : users) { 3582 ipw.println("User " + user + ":"); 3583 ipw.increaseIndent(); 3584 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 3585 ipw.println(); 3586 ipw.decreaseIndent(); 3587 } 3588 } 3589 3590 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 3591 String[] args, boolean isCheckinRequest) { 3592 synchronized (userAccounts.cacheLock) { 3593 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 3594 3595 if (isCheckinRequest) { 3596 // This is a checkin request. *Only* upload the account types and the count of each. 3597 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 3598 null, null, ACCOUNTS_TYPE, null, null); 3599 try { 3600 while (cursor.moveToNext()) { 3601 // print type,count 3602 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 3603 } 3604 } finally { 3605 if (cursor != null) { 3606 cursor.close(); 3607 } 3608 } 3609 } else { 3610 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 3611 Process.myUid(), null); 3612 fout.println("Accounts: " + accounts.length); 3613 for (Account account : accounts) { 3614 fout.println(" " + account); 3615 } 3616 3617 // Add debug information. 3618 fout.println(); 3619 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null, 3620 null, null, null, null, DebugDbHelper.TIMESTAMP); 3621 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key"); 3622 fout.println("Accounts History"); 3623 try { 3624 while (cursor.moveToNext()) { 3625 // print type,count 3626 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," + 3627 cursor.getString(2) + "," + cursor.getString(3) + "," 3628 + cursor.getString(4) + "," + cursor.getString(5)); 3629 } 3630 } finally { 3631 cursor.close(); 3632 } 3633 3634 fout.println(); 3635 synchronized (mSessions) { 3636 final long now = SystemClock.elapsedRealtime(); 3637 fout.println("Active Sessions: " + mSessions.size()); 3638 for (Session session : mSessions.values()) { 3639 fout.println(" " + session.toDebugString(now)); 3640 } 3641 } 3642 3643 fout.println(); 3644 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 3645 } 3646 } 3647 } 3648 3649 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 3650 Intent intent, int userId) { 3651 long identityToken = clearCallingIdentity(); 3652 try { 3653 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3654 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 3655 } 3656 3657 if (intent.getComponent() != null && 3658 GrantCredentialsPermissionActivity.class.getName().equals( 3659 intent.getComponent().getClassName())) { 3660 createNoCredentialsPermissionNotification(account, intent, userId); 3661 } else { 3662 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 3663 intent.addCategory(String.valueOf(notificationId)); 3664 UserHandle user = new UserHandle(userId); 3665 Context contextForUser = getContextForUser(user); 3666 final String notificationTitleFormat = 3667 contextForUser.getText(R.string.notification_title).toString(); 3668 Notification n = new Notification.Builder(contextForUser) 3669 .setWhen(0) 3670 .setSmallIcon(android.R.drawable.stat_sys_warning) 3671 .setColor(contextForUser.getColor( 3672 com.android.internal.R.color.system_notification_accent_color)) 3673 .setContentTitle(String.format(notificationTitleFormat, account.name)) 3674 .setContentText(message) 3675 .setContentIntent(PendingIntent.getActivityAsUser( 3676 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 3677 null, user)) 3678 .build(); 3679 installNotification(notificationId, n, user); 3680 } 3681 } finally { 3682 restoreCallingIdentity(identityToken); 3683 } 3684 } 3685 3686 protected void installNotification(final int notificationId, final Notification n, 3687 UserHandle user) { 3688 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 3689 .notifyAsUser(null, notificationId, n, user); 3690 } 3691 3692 protected void cancelNotification(int id, UserHandle user) { 3693 long identityToken = clearCallingIdentity(); 3694 try { 3695 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 3696 .cancelAsUser(null, id, user); 3697 } finally { 3698 restoreCallingIdentity(identityToken); 3699 } 3700 } 3701 3702 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { 3703 for (String perm : permissions) { 3704 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 3705 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3706 Log.v(TAG, " caller uid " + callingUid + " has " + perm); 3707 } 3708 final int opCode = AppOpsManager.permissionToOpCode(perm); 3709 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( 3710 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { 3711 return true; 3712 } 3713 } 3714 } 3715 return false; 3716 } 3717 3718 private int handleIncomingUser(int userId) { 3719 try { 3720 return ActivityManagerNative.getDefault().handleIncomingUser( 3721 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 3722 } catch (RemoteException re) { 3723 // Shouldn't happen, local. 3724 } 3725 return userId; 3726 } 3727 3728 private boolean isPrivileged(int callingUid) { 3729 final int callingUserId = UserHandle.getUserId(callingUid); 3730 3731 final PackageManager userPackageManager; 3732 try { 3733 userPackageManager = mContext.createPackageContextAsUser( 3734 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 3735 } catch (NameNotFoundException e) { 3736 return false; 3737 } 3738 3739 String[] packages = userPackageManager.getPackagesForUid(callingUid); 3740 for (String name : packages) { 3741 try { 3742 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 3743 if (packageInfo != null 3744 && (packageInfo.applicationInfo.privateFlags 3745 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 3746 return true; 3747 } 3748 } catch (PackageManager.NameNotFoundException e) { 3749 return false; 3750 } 3751 } 3752 return false; 3753 } 3754 3755 private boolean permissionIsGranted( 3756 Account account, String authTokenType, int callerUid, int userId) { 3757 final boolean isPrivileged = isPrivileged(callerUid); 3758 final boolean fromAuthenticator = account != null 3759 && isAccountManagedByCaller(account.type, callerUid, userId); 3760 final boolean hasExplicitGrants = account != null 3761 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); 3762 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3763 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " 3764 + callerUid + ", " + account 3765 + ": is authenticator? " + fromAuthenticator 3766 + ", has explicit permission? " + hasExplicitGrants); 3767 } 3768 return fromAuthenticator || hasExplicitGrants || isPrivileged; 3769 } 3770 3771 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, 3772 String opPackageName) { 3773 if (accountType == null) { 3774 return false; 3775 } else { 3776 return getTypesVisibleToCaller(callingUid, userId, 3777 opPackageName).contains(accountType); 3778 } 3779 } 3780 3781 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { 3782 if (accountType == null) { 3783 return false; 3784 } else { 3785 return getTypesManagedByCaller(callingUid, userId).contains(accountType); 3786 } 3787 } 3788 3789 private List<String> getTypesVisibleToCaller(int callingUid, int userId, 3790 String opPackageName) { 3791 boolean isPermitted = 3792 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, 3793 Manifest.permission.GET_ACCOUNTS_PRIVILEGED); 3794 return getTypesForCaller(callingUid, userId, isPermitted); 3795 } 3796 3797 private List<String> getTypesManagedByCaller(int callingUid, int userId) { 3798 return getTypesForCaller(callingUid, userId, false); 3799 } 3800 3801 private List<String> getTypesForCaller( 3802 int callingUid, int userId, boolean isOtherwisePermitted) { 3803 List<String> managedAccountTypes = new ArrayList<>(); 3804 long identityToken = Binder.clearCallingIdentity(); 3805 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 3806 try { 3807 serviceInfos = mAuthenticatorCache.getAllServices(userId); 3808 } finally { 3809 Binder.restoreCallingIdentity(identityToken); 3810 } 3811 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 3812 serviceInfos) { 3813 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); 3814 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { 3815 managedAccountTypes.add(serviceInfo.type.type); 3816 } 3817 } 3818 return managedAccountTypes; 3819 } 3820 3821 private boolean isAccountPresentForCaller(String accountName, String accountType) { 3822 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) { 3823 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) { 3824 if (account.name.equals(accountName)) { 3825 return true; 3826 } 3827 } 3828 } 3829 return false; 3830 } 3831 3832 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 3833 int callerUid) { 3834 if (callerUid == Process.SYSTEM_UID) { 3835 return true; 3836 } 3837 UserAccounts accounts = getUserAccountsForCaller(); 3838 synchronized (accounts.cacheLock) { 3839 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3840 String[] args = { String.valueOf(callerUid), authTokenType, 3841 account.name, account.type}; 3842 final boolean permissionGranted = 3843 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; 3844 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 3845 // TODO: Skip this check when running automated tests. Replace this 3846 // with a more general solution. 3847 Log.d(TAG, "no credentials permission for usage of " + account + ", " 3848 + authTokenType + " by uid " + callerUid 3849 + " but ignoring since device is in test harness."); 3850 return true; 3851 } 3852 return permissionGranted; 3853 } 3854 } 3855 3856 private boolean isSystemUid(int callingUid) { 3857 String[] packages = null; 3858 long ident = Binder.clearCallingIdentity(); 3859 try { 3860 packages = mPackageManager.getPackagesForUid(callingUid); 3861 } finally { 3862 Binder.restoreCallingIdentity(ident); 3863 } 3864 if (packages != null) { 3865 for (String name : packages) { 3866 try { 3867 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); 3868 if (packageInfo != null 3869 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 3870 != 0) { 3871 return true; 3872 } 3873 } catch (PackageManager.NameNotFoundException e) { 3874 Log.w(TAG, String.format("Could not find package [%s]", name), e); 3875 } 3876 } 3877 } else { 3878 Log.w(TAG, "No known packages with uid " + callingUid); 3879 } 3880 return false; 3881 } 3882 3883 /** Succeeds if any of the specified permissions are granted. */ 3884 private void checkReadAccountsPermitted( 3885 int callingUid, 3886 String accountType, 3887 int userId, 3888 String opPackageName) { 3889 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { 3890 String msg = String.format( 3891 "caller uid %s cannot access %s accounts", 3892 callingUid, 3893 accountType); 3894 Log.w(TAG, " " + msg); 3895 throw new SecurityException(msg); 3896 } 3897 } 3898 3899 private boolean canUserModifyAccounts(int userId) { 3900 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 3901 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 3902 return false; 3903 } 3904 return true; 3905 } 3906 3907 private boolean canUserModifyAccountsForType(int userId, String accountType) { 3908 DevicePolicyManager dpm = (DevicePolicyManager) mContext 3909 .getSystemService(Context.DEVICE_POLICY_SERVICE); 3910 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 3911 if (typesArray == null) { 3912 return true; 3913 } 3914 for (String forbiddenType : typesArray) { 3915 if (forbiddenType.equals(accountType)) { 3916 return false; 3917 } 3918 } 3919 return true; 3920 } 3921 3922 @Override 3923 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 3924 throws RemoteException { 3925 final int callingUid = getCallingUid(); 3926 3927 if (callingUid != Process.SYSTEM_UID) { 3928 throw new SecurityException(); 3929 } 3930 3931 if (value) { 3932 grantAppPermission(account, authTokenType, uid); 3933 } else { 3934 revokeAppPermission(account, authTokenType, uid); 3935 } 3936 } 3937 3938 /** 3939 * Allow callers with the given uid permission to get credentials for account/authTokenType. 3940 * <p> 3941 * Although this is public it can only be accessed via the AccountManagerService object 3942 * which is in the system. This means we don't need to protect it with permissions. 3943 * @hide 3944 */ 3945 private void grantAppPermission(Account account, String authTokenType, int uid) { 3946 if (account == null || authTokenType == null) { 3947 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 3948 return; 3949 } 3950 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3951 synchronized (accounts.cacheLock) { 3952 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3953 db.beginTransaction(); 3954 try { 3955 long accountId = getAccountIdLocked(db, account); 3956 if (accountId >= 0) { 3957 ContentValues values = new ContentValues(); 3958 values.put(GRANTS_ACCOUNTS_ID, accountId); 3959 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 3960 values.put(GRANTS_GRANTEE_UID, uid); 3961 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 3962 db.setTransactionSuccessful(); 3963 } 3964 } finally { 3965 db.endTransaction(); 3966 } 3967 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 3968 new UserHandle(accounts.userId)); 3969 } 3970 } 3971 3972 /** 3973 * Don't allow callers with the given uid permission to get credentials for 3974 * account/authTokenType. 3975 * <p> 3976 * Although this is public it can only be accessed via the AccountManagerService object 3977 * which is in the system. This means we don't need to protect it with permissions. 3978 * @hide 3979 */ 3980 private void revokeAppPermission(Account account, String authTokenType, int uid) { 3981 if (account == null || authTokenType == null) { 3982 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 3983 return; 3984 } 3985 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3986 synchronized (accounts.cacheLock) { 3987 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3988 db.beginTransaction(); 3989 try { 3990 long accountId = getAccountIdLocked(db, account); 3991 if (accountId >= 0) { 3992 db.delete(TABLE_GRANTS, 3993 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 3994 + GRANTS_GRANTEE_UID + "=?", 3995 new String[]{String.valueOf(accountId), authTokenType, 3996 String.valueOf(uid)}); 3997 db.setTransactionSuccessful(); 3998 } 3999 } finally { 4000 db.endTransaction(); 4001 } 4002 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 4003 new UserHandle(accounts.userId)); 4004 } 4005 } 4006 4007 static final private String stringArrayToString(String[] value) { 4008 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 4009 } 4010 4011 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 4012 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 4013 if (oldAccountsForType != null) { 4014 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 4015 for (Account curAccount : oldAccountsForType) { 4016 if (!curAccount.equals(account)) { 4017 newAccountsList.add(curAccount); 4018 } 4019 } 4020 if (newAccountsList.isEmpty()) { 4021 accounts.accountCache.remove(account.type); 4022 } else { 4023 Account[] newAccountsForType = new Account[newAccountsList.size()]; 4024 newAccountsForType = newAccountsList.toArray(newAccountsForType); 4025 accounts.accountCache.put(account.type, newAccountsForType); 4026 } 4027 } 4028 accounts.userDataCache.remove(account); 4029 accounts.authTokenCache.remove(account); 4030 accounts.previousNameCache.remove(account); 4031 } 4032 4033 /** 4034 * This assumes that the caller has already checked that the account is not already present. 4035 */ 4036 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 4037 Account[] accountsForType = accounts.accountCache.get(account.type); 4038 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 4039 Account[] newAccountsForType = new Account[oldLength + 1]; 4040 if (accountsForType != null) { 4041 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 4042 } 4043 newAccountsForType[oldLength] = account; 4044 accounts.accountCache.put(account.type, newAccountsForType); 4045 } 4046 4047 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 4048 int callingUid, String callingPackage) { 4049 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 4050 || callingUid == Process.myUid()) { 4051 return unfiltered; 4052 } 4053 UserInfo user = mUserManager.getUserInfo(userAccounts.userId); 4054 if (user != null && user.isRestricted()) { 4055 String[] packages = mPackageManager.getPackagesForUid(callingUid); 4056 // If any of the packages is a white listed package, return the full set, 4057 // otherwise return non-shared accounts only. 4058 // This might be a temporary way to specify a whitelist 4059 String whiteList = mContext.getResources().getString( 4060 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 4061 for (String packageName : packages) { 4062 if (whiteList.contains(";" + packageName + ";")) { 4063 return unfiltered; 4064 } 4065 } 4066 ArrayList<Account> allowed = new ArrayList<Account>(); 4067 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 4068 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 4069 String requiredAccountType = ""; 4070 try { 4071 // If there's an explicit callingPackage specified, check if that package 4072 // opted in to see restricted accounts. 4073 if (callingPackage != null) { 4074 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 4075 if (pi != null && pi.restrictedAccountType != null) { 4076 requiredAccountType = pi.restrictedAccountType; 4077 } 4078 } else { 4079 // Otherwise check if the callingUid has a package that has opted in 4080 for (String packageName : packages) { 4081 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 4082 if (pi != null && pi.restrictedAccountType != null) { 4083 requiredAccountType = pi.restrictedAccountType; 4084 break; 4085 } 4086 } 4087 } 4088 } catch (NameNotFoundException nnfe) { 4089 } 4090 for (Account account : unfiltered) { 4091 if (account.type.equals(requiredAccountType)) { 4092 allowed.add(account); 4093 } else { 4094 boolean found = false; 4095 for (Account shared : sharedAccounts) { 4096 if (shared.equals(account)) { 4097 found = true; 4098 break; 4099 } 4100 } 4101 if (!found) { 4102 allowed.add(account); 4103 } 4104 } 4105 } 4106 Account[] filtered = new Account[allowed.size()]; 4107 allowed.toArray(filtered); 4108 return filtered; 4109 } else { 4110 return unfiltered; 4111 } 4112 } 4113 4114 /* 4115 * packageName can be null. If not null, it should be used to filter out restricted accounts 4116 * that the package is not allowed to access. 4117 */ 4118 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 4119 int callingUid, String callingPackage) { 4120 if (accountType != null) { 4121 final Account[] accounts = userAccounts.accountCache.get(accountType); 4122 if (accounts == null) { 4123 return EMPTY_ACCOUNT_ARRAY; 4124 } else { 4125 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 4126 callingUid, callingPackage); 4127 } 4128 } else { 4129 int totalLength = 0; 4130 for (Account[] accounts : userAccounts.accountCache.values()) { 4131 totalLength += accounts.length; 4132 } 4133 if (totalLength == 0) { 4134 return EMPTY_ACCOUNT_ARRAY; 4135 } 4136 Account[] accounts = new Account[totalLength]; 4137 totalLength = 0; 4138 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 4139 System.arraycopy(accountsOfType, 0, accounts, totalLength, 4140 accountsOfType.length); 4141 totalLength += accountsOfType.length; 4142 } 4143 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 4144 } 4145 } 4146 4147 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 4148 Account account, String key, String value) { 4149 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 4150 if (userDataForAccount == null) { 4151 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 4152 accounts.userDataCache.put(account, userDataForAccount); 4153 } 4154 if (value == null) { 4155 userDataForAccount.remove(key); 4156 } else { 4157 userDataForAccount.put(key, value); 4158 } 4159 } 4160 4161 protected String readCachedTokenInternal( 4162 UserAccounts accounts, 4163 Account account, 4164 String tokenType, 4165 String callingPackage, 4166 byte[] pkgSigDigest) { 4167 synchronized (accounts.cacheLock) { 4168 return accounts.accountTokenCaches.get( 4169 account, tokenType, callingPackage, pkgSigDigest); 4170 } 4171 } 4172 4173 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 4174 Account account, String key, String value) { 4175 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 4176 if (authTokensForAccount == null) { 4177 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 4178 accounts.authTokenCache.put(account, authTokensForAccount); 4179 } 4180 if (value == null) { 4181 authTokensForAccount.remove(key); 4182 } else { 4183 authTokensForAccount.put(key, value); 4184 } 4185 } 4186 4187 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 4188 String authTokenType) { 4189 synchronized (accounts.cacheLock) { 4190 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 4191 if (authTokensForAccount == null) { 4192 // need to populate the cache for this account 4193 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 4194 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 4195 accounts.authTokenCache.put(account, authTokensForAccount); 4196 } 4197 return authTokensForAccount.get(authTokenType); 4198 } 4199 } 4200 4201 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { 4202 synchronized (accounts.cacheLock) { 4203 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 4204 if (userDataForAccount == null) { 4205 // need to populate the cache for this account 4206 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 4207 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 4208 accounts.userDataCache.put(account, userDataForAccount); 4209 } 4210 return userDataForAccount.get(key); 4211 } 4212 } 4213 4214 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 4215 final SQLiteDatabase db, Account account) { 4216 HashMap<String, String> userDataForAccount = new HashMap<String, String>(); 4217 Cursor cursor = db.query(TABLE_EXTRAS, 4218 COLUMNS_EXTRAS_KEY_AND_VALUE, 4219 SELECTION_USERDATA_BY_ACCOUNT, 4220 new String[]{account.name, account.type}, 4221 null, null, null); 4222 try { 4223 while (cursor.moveToNext()) { 4224 final String tmpkey = cursor.getString(0); 4225 final String value = cursor.getString(1); 4226 userDataForAccount.put(tmpkey, value); 4227 } 4228 } finally { 4229 cursor.close(); 4230 } 4231 return userDataForAccount; 4232 } 4233 4234 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 4235 final SQLiteDatabase db, Account account) { 4236 HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); 4237 Cursor cursor = db.query(TABLE_AUTHTOKENS, 4238 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 4239 SELECTION_AUTHTOKENS_BY_ACCOUNT, 4240 new String[]{account.name, account.type}, 4241 null, null, null); 4242 try { 4243 while (cursor.moveToNext()) { 4244 final String type = cursor.getString(0); 4245 final String authToken = cursor.getString(1); 4246 authTokensForAccount.put(type, authToken); 4247 } 4248 } finally { 4249 cursor.close(); 4250 } 4251 return authTokensForAccount; 4252 } 4253 4254 private Context getContextForUser(UserHandle user) { 4255 try { 4256 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 4257 } catch (NameNotFoundException e) { 4258 // Default to mContext, not finding the package system is running as is unlikely. 4259 return mContext; 4260 } 4261 } 4262} 4263