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