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