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