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