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