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