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