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