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