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