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