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