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