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