AccountManagerService.java revision 9d8a1048bb666c68402dce031bebfa07c92a42db
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 private void showCantAddAccount(int errorCode, int userId) { 2479 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 2480 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 2481 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2482 long identityToken = clearCallingIdentity(); 2483 try { 2484 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId)); 2485 } finally { 2486 restoreCallingIdentity(identityToken); 2487 } 2488 } 2489 2490 @Override 2491 public void confirmCredentialsAsUser( 2492 IAccountManagerResponse response, 2493 final Account account, 2494 final Bundle options, 2495 final boolean expectActivityLaunch, 2496 int userId) { 2497 int callingUid = Binder.getCallingUid(); 2498 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2499 Log.v(TAG, "confirmCredentials: " + account 2500 + ", response " + response 2501 + ", expectActivityLaunch " + expectActivityLaunch 2502 + ", caller's uid " + callingUid 2503 + ", pid " + Binder.getCallingPid()); 2504 } 2505 // Only allow the system process to read accounts of other users 2506 if (isCrossUser(callingUid, userId)) { 2507 throw new SecurityException( 2508 String.format( 2509 "User %s trying to confirm account credentials for %s" , 2510 UserHandle.getCallingUserId(), 2511 userId)); 2512 } 2513 if (response == null) throw new IllegalArgumentException("response is null"); 2514 if (account == null) throw new IllegalArgumentException("account is null"); 2515 long identityToken = clearCallingIdentity(); 2516 try { 2517 UserAccounts accounts = getUserAccounts(userId); 2518 new Session(accounts, response, account.type, expectActivityLaunch, 2519 true /* stripAuthTokenFromResult */, account.name, 2520 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) { 2521 @Override 2522 public void run() throws RemoteException { 2523 mAuthenticator.confirmCredentials(this, account, options); 2524 } 2525 @Override 2526 protected String toDebugString(long now) { 2527 return super.toDebugString(now) + ", confirmCredentials" 2528 + ", " + account; 2529 } 2530 }.bind(); 2531 } finally { 2532 restoreCallingIdentity(identityToken); 2533 } 2534 } 2535 2536 @Override 2537 public void updateCredentials(IAccountManagerResponse response, final Account account, 2538 final String authTokenType, final boolean expectActivityLaunch, 2539 final Bundle loginOptions) { 2540 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2541 Log.v(TAG, "updateCredentials: " + account 2542 + ", response " + response 2543 + ", authTokenType " + authTokenType 2544 + ", expectActivityLaunch " + expectActivityLaunch 2545 + ", caller's uid " + Binder.getCallingUid() 2546 + ", pid " + Binder.getCallingPid()); 2547 } 2548 if (response == null) throw new IllegalArgumentException("response is null"); 2549 if (account == null) throw new IllegalArgumentException("account is null"); 2550 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2551 int userId = UserHandle.getCallingUserId(); 2552 long identityToken = clearCallingIdentity(); 2553 try { 2554 UserAccounts accounts = getUserAccounts(userId); 2555 new Session(accounts, response, account.type, expectActivityLaunch, 2556 true /* stripAuthTokenFromResult */, account.name, 2557 false /* authDetailsRequired */, true /* updateLastCredentialTime */) { 2558 @Override 2559 public void run() throws RemoteException { 2560 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 2561 } 2562 @Override 2563 protected String toDebugString(long now) { 2564 if (loginOptions != null) loginOptions.keySet(); 2565 return super.toDebugString(now) + ", updateCredentials" 2566 + ", " + account 2567 + ", authTokenType " + authTokenType 2568 + ", loginOptions " + loginOptions; 2569 } 2570 }.bind(); 2571 } finally { 2572 restoreCallingIdentity(identityToken); 2573 } 2574 } 2575 2576 @Override 2577 public void startUpdateCredentialsSession( 2578 IAccountManagerResponse response, 2579 final Account account, 2580 final String authTokenType, 2581 final boolean expectActivityLaunch, 2582 final Bundle loginOptions) { 2583 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2584 Log.v(TAG, 2585 "startUpdateCredentialsSession: " + account + ", response " + response 2586 + ", authTokenType " + authTokenType + ", expectActivityLaunch " 2587 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid() 2588 + ", pid " + Binder.getCallingPid()); 2589 } 2590 if (response == null) { 2591 throw new IllegalArgumentException("response is null"); 2592 } 2593 if (account == null) { 2594 throw new IllegalArgumentException("account is null"); 2595 } 2596 int userId = UserHandle.getCallingUserId(); 2597 long identityToken = clearCallingIdentity(); 2598 try { 2599 UserAccounts accounts = getUserAccounts(userId); 2600 new StartAccountSession( 2601 accounts, 2602 response, 2603 account.type, 2604 expectActivityLaunch, 2605 account.name, 2606 false /* authDetailsRequired */, 2607 true /* updateLastCredentialTime */) { 2608 @Override 2609 public void run() throws RemoteException { 2610 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, 2611 loginOptions); 2612 } 2613 2614 @Override 2615 protected String toDebugString(long now) { 2616 if (loginOptions != null) 2617 loginOptions.keySet(); 2618 return super.toDebugString(now) 2619 + ", startUpdateCredentialsSession" 2620 + ", " + account 2621 + ", authTokenType " + authTokenType 2622 + ", loginOptions " + loginOptions; 2623 } 2624 }.bind(); 2625 } finally { 2626 restoreCallingIdentity(identityToken); 2627 } 2628 } 2629 2630 @Override 2631 public void editProperties(IAccountManagerResponse response, final String accountType, 2632 final boolean expectActivityLaunch) { 2633 final int callingUid = Binder.getCallingUid(); 2634 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2635 Log.v(TAG, "editProperties: accountType " + accountType 2636 + ", response " + response 2637 + ", expectActivityLaunch " + expectActivityLaunch 2638 + ", caller's uid " + callingUid 2639 + ", pid " + Binder.getCallingPid()); 2640 } 2641 if (response == null) throw new IllegalArgumentException("response is null"); 2642 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2643 int userId = UserHandle.getCallingUserId(); 2644 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { 2645 String msg = String.format( 2646 "uid %s cannot edit authenticator properites for account type: %s", 2647 callingUid, 2648 accountType); 2649 throw new SecurityException(msg); 2650 } 2651 long identityToken = clearCallingIdentity(); 2652 try { 2653 UserAccounts accounts = getUserAccounts(userId); 2654 new Session(accounts, response, accountType, expectActivityLaunch, 2655 true /* stripAuthTokenFromResult */, null /* accountName */, 2656 false /* authDetailsRequired */) { 2657 @Override 2658 public void run() throws RemoteException { 2659 mAuthenticator.editProperties(this, mAccountType); 2660 } 2661 @Override 2662 protected String toDebugString(long now) { 2663 return super.toDebugString(now) + ", editProperties" 2664 + ", accountType " + accountType; 2665 } 2666 }.bind(); 2667 } finally { 2668 restoreCallingIdentity(identityToken); 2669 } 2670 } 2671 2672 private class GetAccountsByTypeAndFeatureSession extends Session { 2673 private final String[] mFeatures; 2674 private volatile Account[] mAccountsOfType = null; 2675 private volatile ArrayList<Account> mAccountsWithFeatures = null; 2676 private volatile int mCurrentAccount = 0; 2677 private final int mCallingUid; 2678 2679 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 2680 IAccountManagerResponse response, String type, String[] features, int callingUid) { 2681 super(accounts, response, type, false /* expectActivityLaunch */, 2682 true /* stripAuthTokenFromResult */, null /* accountName */, 2683 false /* authDetailsRequired */); 2684 mCallingUid = callingUid; 2685 mFeatures = features; 2686 } 2687 2688 @Override 2689 public void run() throws RemoteException { 2690 synchronized (mAccounts.cacheLock) { 2691 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 2692 null); 2693 } 2694 // check whether each account matches the requested features 2695 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 2696 mCurrentAccount = 0; 2697 2698 checkAccount(); 2699 } 2700 2701 public void checkAccount() { 2702 if (mCurrentAccount >= mAccountsOfType.length) { 2703 sendResult(); 2704 return; 2705 } 2706 2707 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 2708 if (accountAuthenticator == null) { 2709 // It is possible that the authenticator has died, which is indicated by 2710 // mAuthenticator being set to null. If this happens then just abort. 2711 // There is no need to send back a result or error in this case since 2712 // that already happened when mAuthenticator was cleared. 2713 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2714 Log.v(TAG, "checkAccount: aborting session since we are no longer" 2715 + " connected to the authenticator, " + toDebugString()); 2716 } 2717 return; 2718 } 2719 try { 2720 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 2721 } catch (RemoteException e) { 2722 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 2723 } 2724 } 2725 2726 @Override 2727 public void onResult(Bundle result) { 2728 mNumResults++; 2729 if (result == null) { 2730 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 2731 return; 2732 } 2733 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 2734 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 2735 } 2736 mCurrentAccount++; 2737 checkAccount(); 2738 } 2739 2740 public void sendResult() { 2741 IAccountManagerResponse response = getResponseAndClose(); 2742 if (response != null) { 2743 try { 2744 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 2745 for (int i = 0; i < accounts.length; i++) { 2746 accounts[i] = mAccountsWithFeatures.get(i); 2747 } 2748 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2749 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2750 + response); 2751 } 2752 Bundle result = new Bundle(); 2753 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2754 response.onResult(result); 2755 } catch (RemoteException e) { 2756 // if the caller is dead then there is no one to care about remote exceptions 2757 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2758 Log.v(TAG, "failure while notifying response", e); 2759 } 2760 } 2761 } 2762 } 2763 2764 2765 @Override 2766 protected String toDebugString(long now) { 2767 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 2768 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 2769 } 2770 } 2771 2772 /** 2773 * Returns the accounts visible to the client within the context of a specific user 2774 * @hide 2775 */ 2776 @NonNull 2777 public Account[] getAccounts(int userId, String opPackageName) { 2778 int callingUid = Binder.getCallingUid(); 2779 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 2780 opPackageName); 2781 if (visibleAccountTypes.isEmpty()) { 2782 return new Account[0]; 2783 } 2784 long identityToken = clearCallingIdentity(); 2785 try { 2786 UserAccounts accounts = getUserAccounts(userId); 2787 return getAccountsInternal( 2788 accounts, 2789 callingUid, 2790 null, // packageName 2791 visibleAccountTypes); 2792 } finally { 2793 restoreCallingIdentity(identityToken); 2794 } 2795 } 2796 2797 /** 2798 * Returns accounts for all running users. 2799 * 2800 * @hide 2801 */ 2802 @NonNull 2803 public AccountAndUser[] getRunningAccounts() { 2804 final int[] runningUserIds; 2805 try { 2806 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 2807 } catch (RemoteException e) { 2808 // Running in system_server; should never happen 2809 throw new RuntimeException(e); 2810 } 2811 return getAccounts(runningUserIds); 2812 } 2813 2814 /** {@hide} */ 2815 @NonNull 2816 public AccountAndUser[] getAllAccounts() { 2817 final List<UserInfo> users = getUserManager().getUsers(); 2818 final int[] userIds = new int[users.size()]; 2819 for (int i = 0; i < userIds.length; i++) { 2820 userIds[i] = users.get(i).id; 2821 } 2822 return getAccounts(userIds); 2823 } 2824 2825 @NonNull 2826 private AccountAndUser[] getAccounts(int[] userIds) { 2827 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 2828 for (int userId : userIds) { 2829 UserAccounts userAccounts = getUserAccounts(userId); 2830 if (userAccounts == null) continue; 2831 synchronized (userAccounts.cacheLock) { 2832 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 2833 Binder.getCallingUid(), null); 2834 for (int a = 0; a < accounts.length; a++) { 2835 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 2836 } 2837 } 2838 } 2839 2840 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 2841 return runningAccounts.toArray(accountsArray); 2842 } 2843 2844 @Override 2845 @NonNull 2846 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { 2847 return getAccountsAsUser(type, userId, null, -1, opPackageName); 2848 } 2849 2850 @NonNull 2851 private Account[] getAccountsAsUser( 2852 String type, 2853 int userId, 2854 String callingPackage, 2855 int packageUid, 2856 String opPackageName) { 2857 int callingUid = Binder.getCallingUid(); 2858 // Only allow the system process to read accounts of other users 2859 if (userId != UserHandle.getCallingUserId() 2860 && callingUid != Process.myUid() 2861 && mContext.checkCallingOrSelfPermission( 2862 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 2863 != PackageManager.PERMISSION_GRANTED) { 2864 throw new SecurityException("User " + UserHandle.getCallingUserId() 2865 + " trying to get account for " + userId); 2866 } 2867 2868 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2869 Log.v(TAG, "getAccounts: accountType " + type 2870 + ", caller's uid " + Binder.getCallingUid() 2871 + ", pid " + Binder.getCallingPid()); 2872 } 2873 // If the original calling app was using the framework account chooser activity, we'll 2874 // be passed in the original caller's uid here, which is what should be used for filtering. 2875 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 2876 callingUid = packageUid; 2877 opPackageName = callingPackage; 2878 } 2879 2880 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 2881 opPackageName); 2882 if (visibleAccountTypes.isEmpty() 2883 || (type != null && !visibleAccountTypes.contains(type))) { 2884 return new Account[0]; 2885 } else if (visibleAccountTypes.contains(type)) { 2886 // Prune the list down to just the requested type. 2887 visibleAccountTypes = new ArrayList<>(); 2888 visibleAccountTypes.add(type); 2889 } // else aggregate all the visible accounts (it won't matter if the 2890 // list is empty). 2891 2892 long identityToken = clearCallingIdentity(); 2893 try { 2894 UserAccounts accounts = getUserAccounts(userId); 2895 return getAccountsInternal( 2896 accounts, 2897 callingUid, 2898 callingPackage, 2899 visibleAccountTypes); 2900 } finally { 2901 restoreCallingIdentity(identityToken); 2902 } 2903 } 2904 2905 @NonNull 2906 private Account[] getAccountsInternal( 2907 UserAccounts userAccounts, 2908 int callingUid, 2909 String callingPackage, 2910 List<String> visibleAccountTypes) { 2911 synchronized (userAccounts.cacheLock) { 2912 ArrayList<Account> visibleAccounts = new ArrayList<>(); 2913 for (String visibleType : visibleAccountTypes) { 2914 Account[] accountsForType = getAccountsFromCacheLocked( 2915 userAccounts, visibleType, callingUid, callingPackage); 2916 if (accountsForType != null) { 2917 visibleAccounts.addAll(Arrays.asList(accountsForType)); 2918 } 2919 } 2920 Account[] result = new Account[visibleAccounts.size()]; 2921 for (int i = 0; i < visibleAccounts.size(); i++) { 2922 result[i] = visibleAccounts.get(i); 2923 } 2924 return result; 2925 } 2926 } 2927 2928 @Override 2929 public void addSharedAccountsFromParentUser(int parentUserId, int userId) { 2930 checkManageUsersPermission("addSharedAccountsFromParentUser"); 2931 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName()); 2932 for (Account account : accounts) { 2933 addSharedAccountAsUser(account, userId); 2934 } 2935 } 2936 2937 private boolean addSharedAccountAsUser(Account account, int userId) { 2938 userId = handleIncomingUser(userId); 2939 UserAccounts accounts = getUserAccounts(userId); 2940 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2941 ContentValues values = new ContentValues(); 2942 values.put(ACCOUNTS_NAME, account.name); 2943 values.put(ACCOUNTS_TYPE, account.type); 2944 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2945 new String[] {account.name, account.type}); 2946 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 2947 if (accountId < 0) { 2948 Log.w(TAG, "insertAccountIntoDatabase: " + account 2949 + ", skipping the DB insert failed"); 2950 return false; 2951 } 2952 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts); 2953 return true; 2954 } 2955 2956 @Override 2957 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 2958 userId = handleIncomingUser(userId); 2959 UserAccounts accounts = getUserAccounts(userId); 2960 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2961 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 2962 final ContentValues values = new ContentValues(); 2963 values.put(ACCOUNTS_NAME, newName); 2964 values.put(ACCOUNTS_PREVIOUS_NAME, account.name); 2965 int r = db.update( 2966 TABLE_SHARED_ACCOUNTS, 2967 values, 2968 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2969 new String[] { account.name, account.type }); 2970 if (r > 0) { 2971 int callingUid = getCallingUid(); 2972 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS, 2973 sharedTableAccountId, accounts, callingUid); 2974 // Recursively rename the account. 2975 renameAccountInternal(accounts, account, newName); 2976 } 2977 return r > 0; 2978 } 2979 2980 @Override 2981 public boolean removeSharedAccountAsUser(Account account, int userId) { 2982 return removeSharedAccountAsUser(account, userId, getCallingUid()); 2983 } 2984 2985 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) { 2986 userId = handleIncomingUser(userId); 2987 UserAccounts accounts = getUserAccounts(userId); 2988 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2989 long sharedTableAccountId = getAccountIdFromSharedTable(db, account); 2990 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2991 new String[] {account.name, account.type}); 2992 if (r > 0) { 2993 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS, 2994 sharedTableAccountId, accounts, callingUid); 2995 removeAccountInternal(accounts, account, callingUid); 2996 } 2997 return r > 0; 2998 } 2999 3000 @Override 3001 public Account[] getSharedAccountsAsUser(int userId) { 3002 userId = handleIncomingUser(userId); 3003 UserAccounts accounts = getUserAccounts(userId); 3004 ArrayList<Account> accountList = new ArrayList<Account>(); 3005 Cursor cursor = null; 3006 try { 3007 cursor = accounts.openHelper.getReadableDatabase() 3008 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 3009 null, null, null, null, null); 3010 if (cursor != null && cursor.moveToFirst()) { 3011 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 3012 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 3013 do { 3014 accountList.add(new Account(cursor.getString(nameIndex), 3015 cursor.getString(typeIndex))); 3016 } while (cursor.moveToNext()); 3017 } 3018 } finally { 3019 if (cursor != null) { 3020 cursor.close(); 3021 } 3022 } 3023 Account[] accountArray = new Account[accountList.size()]; 3024 accountList.toArray(accountArray); 3025 return accountArray; 3026 } 3027 3028 @Override 3029 @NonNull 3030 public Account[] getAccounts(String type, String opPackageName) { 3031 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName); 3032 } 3033 3034 @Override 3035 @NonNull 3036 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { 3037 int callingUid = Binder.getCallingUid(); 3038 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 3039 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 3040 + callingUid + " with uid=" + uid); 3041 } 3042 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid, 3043 opPackageName); 3044 } 3045 3046 @Override 3047 @NonNull 3048 public Account[] getAccountsByTypeForPackage(String type, String packageName, 3049 String opPackageName) { 3050 int packageUid = -1; 3051 try { 3052 packageUid = AppGlobals.getPackageManager().getPackageUid( 3053 packageName, UserHandle.getCallingUserId()); 3054 } catch (RemoteException re) { 3055 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 3056 return new Account[0]; 3057 } 3058 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, 3059 packageUid, opPackageName); 3060 } 3061 3062 @Override 3063 public void getAccountsByFeatures( 3064 IAccountManagerResponse response, 3065 String type, 3066 String[] features, 3067 String opPackageName) { 3068 int callingUid = Binder.getCallingUid(); 3069 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3070 Log.v(TAG, "getAccounts: accountType " + type 3071 + ", response " + response 3072 + ", features " + stringArrayToString(features) 3073 + ", caller's uid " + callingUid 3074 + ", pid " + Binder.getCallingPid()); 3075 } 3076 if (response == null) throw new IllegalArgumentException("response is null"); 3077 if (type == null) throw new IllegalArgumentException("accountType is null"); 3078 int userId = UserHandle.getCallingUserId(); 3079 3080 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, 3081 opPackageName); 3082 if (!visibleAccountTypes.contains(type)) { 3083 Bundle result = new Bundle(); 3084 // Need to return just the accounts that are from matching signatures. 3085 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); 3086 try { 3087 response.onResult(result); 3088 } catch (RemoteException e) { 3089 Log.e(TAG, "Cannot respond to caller do to exception." , e); 3090 } 3091 return; 3092 } 3093 long identityToken = clearCallingIdentity(); 3094 try { 3095 UserAccounts userAccounts = getUserAccounts(userId); 3096 if (features == null || features.length == 0) { 3097 Account[] accounts; 3098 synchronized (userAccounts.cacheLock) { 3099 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 3100 } 3101 Bundle result = new Bundle(); 3102 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 3103 onResult(response, result); 3104 return; 3105 } 3106 new GetAccountsByTypeAndFeatureSession( 3107 userAccounts, 3108 response, 3109 type, 3110 features, 3111 callingUid).bind(); 3112 } finally { 3113 restoreCallingIdentity(identityToken); 3114 } 3115 } 3116 3117 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) { 3118 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID}, 3119 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 3120 try { 3121 if (cursor.moveToNext()) { 3122 return cursor.getLong(0); 3123 } 3124 return -1; 3125 } finally { 3126 cursor.close(); 3127 } 3128 } 3129 3130 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 3131 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 3132 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 3133 try { 3134 if (cursor.moveToNext()) { 3135 return cursor.getLong(0); 3136 } 3137 return -1; 3138 } finally { 3139 cursor.close(); 3140 } 3141 } 3142 3143 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 3144 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, 3145 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 3146 new String[]{key}, null, null, null); 3147 try { 3148 if (cursor.moveToNext()) { 3149 return cursor.getLong(0); 3150 } 3151 return -1; 3152 } finally { 3153 cursor.close(); 3154 } 3155 } 3156 3157 private abstract class Session extends IAccountAuthenticatorResponse.Stub 3158 implements IBinder.DeathRecipient, ServiceConnection { 3159 IAccountManagerResponse mResponse; 3160 final String mAccountType; 3161 final boolean mExpectActivityLaunch; 3162 final long mCreationTime; 3163 final String mAccountName; 3164 // Indicates if we need to add auth details(like last credential time) 3165 final boolean mAuthDetailsRequired; 3166 // If set, we need to update the last authenticated time. This is 3167 // currently 3168 // used on 3169 // successful confirming credentials. 3170 final boolean mUpdateLastAuthenticatedTime; 3171 3172 public int mNumResults = 0; 3173 private int mNumRequestContinued = 0; 3174 private int mNumErrors = 0; 3175 3176 IAccountAuthenticator mAuthenticator = null; 3177 3178 private final boolean mStripAuthTokenFromResult; 3179 protected final UserAccounts mAccounts; 3180 3181 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 3182 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 3183 boolean authDetailsRequired) { 3184 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult, 3185 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */); 3186 } 3187 3188 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 3189 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, 3190 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) { 3191 super(); 3192 //if (response == null) throw new IllegalArgumentException("response is null"); 3193 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 3194 mAccounts = accounts; 3195 mStripAuthTokenFromResult = stripAuthTokenFromResult; 3196 mResponse = response; 3197 mAccountType = accountType; 3198 mExpectActivityLaunch = expectActivityLaunch; 3199 mCreationTime = SystemClock.elapsedRealtime(); 3200 mAccountName = accountName; 3201 mAuthDetailsRequired = authDetailsRequired; 3202 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; 3203 3204 synchronized (mSessions) { 3205 mSessions.put(toString(), this); 3206 } 3207 if (response != null) { 3208 try { 3209 response.asBinder().linkToDeath(this, 0 /* flags */); 3210 } catch (RemoteException e) { 3211 mResponse = null; 3212 binderDied(); 3213 } 3214 } 3215 } 3216 3217 IAccountManagerResponse getResponseAndClose() { 3218 if (mResponse == null) { 3219 // this session has already been closed 3220 return null; 3221 } 3222 IAccountManagerResponse response = mResponse; 3223 close(); // this clears mResponse so we need to save the response before this call 3224 return response; 3225 } 3226 3227 private void close() { 3228 synchronized (mSessions) { 3229 if (mSessions.remove(toString()) == null) { 3230 // the session was already closed, so bail out now 3231 return; 3232 } 3233 } 3234 if (mResponse != null) { 3235 // stop listening for response deaths 3236 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 3237 3238 // clear this so that we don't accidentally send any further results 3239 mResponse = null; 3240 } 3241 cancelTimeout(); 3242 unbind(); 3243 } 3244 3245 @Override 3246 public void binderDied() { 3247 mResponse = null; 3248 close(); 3249 } 3250 3251 protected String toDebugString() { 3252 return toDebugString(SystemClock.elapsedRealtime()); 3253 } 3254 3255 protected String toDebugString(long now) { 3256 return "Session: expectLaunch " + mExpectActivityLaunch 3257 + ", connected " + (mAuthenticator != null) 3258 + ", stats (" + mNumResults + "/" + mNumRequestContinued 3259 + "/" + mNumErrors + ")" 3260 + ", lifetime " + ((now - mCreationTime) / 1000.0); 3261 } 3262 3263 void bind() { 3264 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3265 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 3266 } 3267 if (!bindToAuthenticator(mAccountType)) { 3268 Log.d(TAG, "bind attempt failed for " + toDebugString()); 3269 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 3270 } 3271 } 3272 3273 private void unbind() { 3274 if (mAuthenticator != null) { 3275 mAuthenticator = null; 3276 mContext.unbindService(this); 3277 } 3278 } 3279 3280 public void cancelTimeout() { 3281 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 3282 } 3283 3284 @Override 3285 public void onServiceConnected(ComponentName name, IBinder service) { 3286 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 3287 try { 3288 run(); 3289 } catch (RemoteException e) { 3290 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3291 "remote exception"); 3292 } 3293 } 3294 3295 @Override 3296 public void onServiceDisconnected(ComponentName name) { 3297 mAuthenticator = null; 3298 IAccountManagerResponse response = getResponseAndClose(); 3299 if (response != null) { 3300 try { 3301 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3302 "disconnected"); 3303 } catch (RemoteException e) { 3304 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3305 Log.v(TAG, "Session.onServiceDisconnected: " 3306 + "caught RemoteException while responding", e); 3307 } 3308 } 3309 } 3310 } 3311 3312 public abstract void run() throws RemoteException; 3313 3314 public void onTimedOut() { 3315 IAccountManagerResponse response = getResponseAndClose(); 3316 if (response != null) { 3317 try { 3318 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 3319 "timeout"); 3320 } catch (RemoteException e) { 3321 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3322 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 3323 e); 3324 } 3325 } 3326 } 3327 } 3328 3329 @Override 3330 public void onResult(Bundle result) { 3331 mNumResults++; 3332 Intent intent = null; 3333 if (result != null) { 3334 boolean isSuccessfulConfirmCreds = result.getBoolean( 3335 AccountManager.KEY_BOOLEAN_RESULT, false); 3336 boolean isSuccessfulUpdateCredsOrAddAccount = 3337 result.containsKey(AccountManager.KEY_ACCOUNT_NAME) 3338 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE); 3339 // We should only update lastAuthenticated time, if 3340 // mUpdateLastAuthenticatedTime is true and the confirmRequest 3341 // or updateRequest was successful 3342 boolean needUpdate = mUpdateLastAuthenticatedTime 3343 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount); 3344 if (needUpdate || mAuthDetailsRequired) { 3345 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType); 3346 if (needUpdate && accountPresent) { 3347 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType)); 3348 } 3349 if (mAuthDetailsRequired) { 3350 long lastAuthenticatedTime = -1; 3351 if (accountPresent) { 3352 lastAuthenticatedTime = DatabaseUtils.longForQuery( 3353 mAccounts.openHelper.getReadableDatabase(), 3354 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 3355 + " from " + 3356 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND " 3357 + ACCOUNTS_TYPE + "=?", 3358 new String[] { 3359 mAccountName, mAccountType 3360 }); 3361 } 3362 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME, 3363 lastAuthenticatedTime); 3364 } 3365 } 3366 } 3367 if (result != null 3368 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 3369 /* 3370 * The Authenticator API allows third party authenticators to 3371 * supply arbitrary intents to other apps that they can run, 3372 * this can be very bad when those apps are in the system like 3373 * the System Settings. 3374 */ 3375 int authenticatorUid = Binder.getCallingUid(); 3376 long bid = Binder.clearCallingIdentity(); 3377 try { 3378 PackageManager pm = mContext.getPackageManager(); 3379 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 3380 int targetUid = resolveInfo.activityInfo.applicationInfo.uid; 3381 if (PackageManager.SIGNATURE_MATCH != 3382 pm.checkSignatures(authenticatorUid, targetUid)) { 3383 throw new SecurityException( 3384 "Activity to be started with KEY_INTENT must " + 3385 "share Authenticator's signatures"); 3386 } 3387 } finally { 3388 Binder.restoreCallingIdentity(bid); 3389 } 3390 } 3391 if (result != null 3392 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 3393 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 3394 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 3395 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 3396 Account account = new Account(accountName, accountType); 3397 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 3398 new UserHandle(mAccounts.userId)); 3399 } 3400 } 3401 IAccountManagerResponse response; 3402 if (mExpectActivityLaunch && result != null 3403 && result.containsKey(AccountManager.KEY_INTENT)) { 3404 response = mResponse; 3405 } else { 3406 response = getResponseAndClose(); 3407 } 3408 if (response != null) { 3409 try { 3410 if (result == null) { 3411 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3412 Log.v(TAG, getClass().getSimpleName() 3413 + " calling onError() on response " + response); 3414 } 3415 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 3416 "null bundle returned"); 3417 } else { 3418 if (mStripAuthTokenFromResult) { 3419 result.remove(AccountManager.KEY_AUTHTOKEN); 3420 } 3421 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3422 Log.v(TAG, getClass().getSimpleName() 3423 + " calling onResult() on response " + response); 3424 } 3425 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 3426 (intent == null)) { 3427 // All AccountManager error codes are greater than 0 3428 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 3429 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 3430 } else { 3431 response.onResult(result); 3432 } 3433 } 3434 } catch (RemoteException e) { 3435 // if the caller is dead then there is no one to care about remote exceptions 3436 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3437 Log.v(TAG, "failure while notifying response", e); 3438 } 3439 } 3440 } 3441 } 3442 3443 @Override 3444 public void onRequestContinued() { 3445 mNumRequestContinued++; 3446 } 3447 3448 @Override 3449 public void onError(int errorCode, String errorMessage) { 3450 mNumErrors++; 3451 IAccountManagerResponse response = getResponseAndClose(); 3452 if (response != null) { 3453 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3454 Log.v(TAG, getClass().getSimpleName() 3455 + " calling onError() on response " + response); 3456 } 3457 try { 3458 response.onError(errorCode, errorMessage); 3459 } catch (RemoteException e) { 3460 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3461 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 3462 } 3463 } 3464 } else { 3465 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3466 Log.v(TAG, "Session.onError: already closed"); 3467 } 3468 } 3469 } 3470 3471 /** 3472 * find the component name for the authenticator and initiate a bind 3473 * if no authenticator or the bind fails then return false, otherwise return true 3474 */ 3475 private boolean bindToAuthenticator(String authenticatorType) { 3476 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 3477 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 3478 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 3479 if (authenticatorInfo == null) { 3480 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3481 Log.v(TAG, "there is no authenticator for " + authenticatorType 3482 + ", bailing out"); 3483 } 3484 return false; 3485 } 3486 3487 final ActivityManager am = mContext.getSystemService(ActivityManager.class); 3488 if (am.isUserRunningAndLocked(mAccounts.userId) 3489 && !authenticatorInfo.componentInfo.encryptionAware) { 3490 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName 3491 + " which isn't encryption aware"); 3492 return false; 3493 } 3494 3495 Intent intent = new Intent(); 3496 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 3497 intent.setComponent(authenticatorInfo.componentName); 3498 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3499 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 3500 } 3501 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 3502 new UserHandle(mAccounts.userId))) { 3503 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3504 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 3505 } 3506 return false; 3507 } 3508 3509 return true; 3510 } 3511 } 3512 3513 private class MessageHandler extends Handler { 3514 MessageHandler(Looper looper) { 3515 super(looper); 3516 } 3517 3518 @Override 3519 public void handleMessage(Message msg) { 3520 switch (msg.what) { 3521 case MESSAGE_TIMED_OUT: 3522 Session session = (Session)msg.obj; 3523 session.onTimedOut(); 3524 break; 3525 3526 case MESSAGE_COPY_SHARED_ACCOUNT: 3527 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); 3528 break; 3529 3530 default: 3531 throw new IllegalStateException("unhandled message: " + msg.what); 3532 } 3533 } 3534 } 3535 3536 private static String getDatabaseName(int userId) { 3537 File systemDir = Environment.getSystemSecureDirectory(); 3538 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); 3539 if (userId == 0) { 3540 // Migrate old file, if it exists, to the new location. 3541 // Make sure the new file doesn't already exist. A dummy file could have been 3542 // accidentally created in the old location, causing the new one to become corrupted 3543 // as well. 3544 File oldFile = new File(systemDir, DATABASE_NAME); 3545 if (oldFile.exists() && !databaseFile.exists()) { 3546 // Check for use directory; create if it doesn't exist, else renameTo will fail 3547 File userDir = Environment.getUserSystemDirectory(userId); 3548 if (!userDir.exists()) { 3549 if (!userDir.mkdirs()) { 3550 throw new IllegalStateException("User dir cannot be created: " + userDir); 3551 } 3552 } 3553 if (!oldFile.renameTo(databaseFile)) { 3554 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 3555 } 3556 } 3557 } 3558 return databaseFile.getPath(); 3559 } 3560 3561 private static class DebugDbHelper{ 3562 private DebugDbHelper() { 3563 } 3564 3565 private static String TABLE_DEBUG = "debug_table"; 3566 3567 // Columns for the table 3568 private static String ACTION_TYPE = "action_type"; 3569 private static String TIMESTAMP = "time"; 3570 private static String CALLER_UID = "caller_uid"; 3571 private static String TABLE_NAME = "table_name"; 3572 private static String KEY = "primary_key"; 3573 3574 // These actions correspond to the occurrence of real actions. Since 3575 // these are called by the authenticators, the uid associated will be 3576 // of the authenticator. 3577 private static String ACTION_SET_PASSWORD = "action_set_password"; 3578 private static String ACTION_CLEAR_PASSWORD = "action_clear_password"; 3579 private static String ACTION_ACCOUNT_ADD = "action_account_add"; 3580 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove"; 3581 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove"; 3582 private static String ACTION_ACCOUNT_RENAME = "action_account_rename"; 3583 3584 // These actions don't necessarily correspond to any action on 3585 // accountDb taking place. As an example, there might be a request for 3586 // addingAccount, which might not lead to addition of account on grounds 3587 // of bad authentication. We will still be logging it to keep track of 3588 // who called. 3589 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add"; 3590 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove"; 3591 3592 // TODO: This action doesn't add account to accountdb. Account is only 3593 // added in finishAddAccount or finishAddAccountAsUser which may be in 3594 // a different user profile. 3595 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add"; 3596 3597 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 3598 3599 private static void createDebugTable(SQLiteDatabase db) { 3600 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " 3601 + ACCOUNTS_ID + " INTEGER," 3602 + ACTION_TYPE + " TEXT NOT NULL, " 3603 + TIMESTAMP + " DATETIME," 3604 + CALLER_UID + " INTEGER NOT NULL," 3605 + TABLE_NAME + " TEXT NOT NULL," 3606 + KEY + " INTEGER PRIMARY KEY)"); 3607 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")"); 3608 } 3609 } 3610 3611 private void logRecord(UserAccounts accounts, String action, String tableName) { 3612 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3613 logRecord(db, action, tableName, -1, accounts); 3614 } 3615 3616 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) { 3617 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3618 logRecord(db, action, tableName, -1, accounts, uid); 3619 } 3620 3621 /* 3622 * This function receives an opened writable database. 3623 */ 3624 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 3625 UserAccounts userAccount) { 3626 logRecord(db, action, tableName, accountId, userAccount, getCallingUid()); 3627 } 3628 3629 /* 3630 * This function receives an opened writable database. 3631 */ 3632 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId, 3633 UserAccounts userAccount, int callingUid) { 3634 SQLiteStatement logStatement = userAccount.statementForLogging; 3635 logStatement.bindLong(1, accountId); 3636 logStatement.bindString(2, action); 3637 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date())); 3638 logStatement.bindLong(4, callingUid); 3639 logStatement.bindString(5, tableName); 3640 logStatement.bindLong(6, userAccount.debugDbInsertionPoint); 3641 logStatement.execute(); 3642 logStatement.clearBindings(); 3643 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1) 3644 % MAX_DEBUG_DB_SIZE; 3645 } 3646 3647 /* 3648 * This should only be called once to compile the sql statement for logging 3649 * and to find the insertion point. 3650 */ 3651 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db, 3652 UserAccounts userAccount) { 3653 // Initialize the count if not done earlier. 3654 int size = (int) getDebugTableRowCount(db); 3655 if (size >= MAX_DEBUG_DB_SIZE) { 3656 // Table is full, and we need to find the point where to insert. 3657 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db); 3658 } else { 3659 userAccount.debugDbInsertionPoint = size; 3660 } 3661 compileSqlStatementForLogging(db, userAccount); 3662 } 3663 3664 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) { 3665 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG 3666 + " VALUES (?,?,?,?,?,?)"; 3667 userAccount.statementForLogging = db.compileStatement(sql); 3668 } 3669 3670 private long getDebugTableRowCount(SQLiteDatabase db) { 3671 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG; 3672 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 3673 } 3674 3675 /* 3676 * Finds the row key where the next insertion should take place. This should 3677 * be invoked only if the table has reached its full capacity. 3678 */ 3679 private long getDebugTableInsertionPoint(SQLiteDatabase db) { 3680 // This query finds the smallest timestamp value (and if 2 records have 3681 // same timestamp, the choose the lower id). 3682 String queryCountDebugDbRows = new StringBuilder() 3683 .append("SELECT ").append(DebugDbHelper.KEY) 3684 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG) 3685 .append(" ORDER BY ") 3686 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY) 3687 .append(" LIMIT 1") 3688 .toString(); 3689 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 3690 } 3691 3692 static class DatabaseHelper extends SQLiteOpenHelper { 3693 3694 public DatabaseHelper(Context context, int userId) { 3695 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); 3696 } 3697 3698 /** 3699 * This call needs to be made while the mCacheLock is held. The way to 3700 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 3701 * @param db The database. 3702 */ 3703 @Override 3704 public void onCreate(SQLiteDatabase db) { 3705 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 3706 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3707 + ACCOUNTS_NAME + " TEXT NOT NULL, " 3708 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 3709 + ACCOUNTS_PASSWORD + " TEXT, " 3710 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 3711 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, " 3712 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 3713 3714 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 3715 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3716 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 3717 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 3718 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 3719 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 3720 3721 createGrantsTable(db); 3722 3723 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 3724 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3725 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 3726 + EXTRAS_KEY + " TEXT NOT NULL, " 3727 + EXTRAS_VALUE + " TEXT, " 3728 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 3729 3730 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 3731 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 3732 + META_VALUE + " TEXT)"); 3733 3734 createSharedAccountsTable(db); 3735 3736 createAccountsDeletionTrigger(db); 3737 3738 DebugDbHelper.createDebugTable(db); 3739 } 3740 3741 private void createSharedAccountsTable(SQLiteDatabase db) { 3742 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 3743 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 3744 + ACCOUNTS_NAME + " TEXT NOT NULL, " 3745 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 3746 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 3747 } 3748 3749 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) { 3750 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " 3751 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0"); 3752 } 3753 3754 private void addOldAccountNameColumn(SQLiteDatabase db) { 3755 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 3756 } 3757 3758 private void addDebugTable(SQLiteDatabase db) { 3759 DebugDbHelper.createDebugTable(db); 3760 } 3761 3762 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 3763 db.execSQL("" 3764 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 3765 + " BEGIN" 3766 + " DELETE FROM " + TABLE_AUTHTOKENS 3767 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3768 + " DELETE FROM " + TABLE_EXTRAS 3769 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3770 + " DELETE FROM " + TABLE_GRANTS 3771 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 3772 + " END"); 3773 } 3774 3775 private void createGrantsTable(SQLiteDatabase db) { 3776 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 3777 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 3778 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 3779 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 3780 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 3781 + "," + GRANTS_GRANTEE_UID + "))"); 3782 } 3783 3784 @Override 3785 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 3786 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 3787 3788 if (oldVersion == 1) { 3789 // no longer need to do anything since the work is done 3790 // when upgrading from version 2 3791 oldVersion++; 3792 } 3793 3794 if (oldVersion == 2) { 3795 createGrantsTable(db); 3796 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 3797 createAccountsDeletionTrigger(db); 3798 oldVersion++; 3799 } 3800 3801 if (oldVersion == 3) { 3802 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 3803 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 3804 oldVersion++; 3805 } 3806 3807 if (oldVersion == 4) { 3808 createSharedAccountsTable(db); 3809 oldVersion++; 3810 } 3811 3812 if (oldVersion == 5) { 3813 addOldAccountNameColumn(db); 3814 oldVersion++; 3815 } 3816 3817 if (oldVersion == 6) { 3818 addLastSuccessfullAuthenticatedTimeColumn(db); 3819 oldVersion++; 3820 } 3821 3822 if (oldVersion == 7) { 3823 addDebugTable(db); 3824 oldVersion++; 3825 } 3826 3827 if (oldVersion != newVersion) { 3828 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 3829 } 3830 } 3831 3832 @Override 3833 public void onOpen(SQLiteDatabase db) { 3834 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 3835 } 3836 } 3837 3838 public IBinder onBind(@SuppressWarnings("unused") Intent intent) { 3839 return asBinder(); 3840 } 3841 3842 /** 3843 * Searches array of arguments for the specified string 3844 * @param args array of argument strings 3845 * @param value value to search for 3846 * @return true if the value is contained in the array 3847 */ 3848 private static boolean scanArgs(String[] args, String value) { 3849 if (args != null) { 3850 for (String arg : args) { 3851 if (value.equals(arg)) { 3852 return true; 3853 } 3854 } 3855 } 3856 return false; 3857 } 3858 3859 @Override 3860 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 3861 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 3862 != PackageManager.PERMISSION_GRANTED) { 3863 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 3864 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 3865 + " without permission " + android.Manifest.permission.DUMP); 3866 return; 3867 } 3868 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 3869 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 3870 3871 final List<UserInfo> users = getUserManager().getUsers(); 3872 for (UserInfo user : users) { 3873 ipw.println("User " + user + ":"); 3874 ipw.increaseIndent(); 3875 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 3876 ipw.println(); 3877 ipw.decreaseIndent(); 3878 } 3879 } 3880 3881 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 3882 String[] args, boolean isCheckinRequest) { 3883 synchronized (userAccounts.cacheLock) { 3884 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 3885 3886 if (isCheckinRequest) { 3887 // This is a checkin request. *Only* upload the account types and the count of each. 3888 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 3889 null, null, ACCOUNTS_TYPE, null, null); 3890 try { 3891 while (cursor.moveToNext()) { 3892 // print type,count 3893 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 3894 } 3895 } finally { 3896 if (cursor != null) { 3897 cursor.close(); 3898 } 3899 } 3900 } else { 3901 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 3902 Process.myUid(), null); 3903 fout.println("Accounts: " + accounts.length); 3904 for (Account account : accounts) { 3905 fout.println(" " + account); 3906 } 3907 3908 // Add debug information. 3909 fout.println(); 3910 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null, 3911 null, null, null, null, DebugDbHelper.TIMESTAMP); 3912 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key"); 3913 fout.println("Accounts History"); 3914 try { 3915 while (cursor.moveToNext()) { 3916 // print type,count 3917 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," + 3918 cursor.getString(2) + "," + cursor.getString(3) + "," 3919 + cursor.getString(4) + "," + cursor.getString(5)); 3920 } 3921 } finally { 3922 cursor.close(); 3923 } 3924 3925 fout.println(); 3926 synchronized (mSessions) { 3927 final long now = SystemClock.elapsedRealtime(); 3928 fout.println("Active Sessions: " + mSessions.size()); 3929 for (Session session : mSessions.values()) { 3930 fout.println(" " + session.toDebugString(now)); 3931 } 3932 } 3933 3934 fout.println(); 3935 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 3936 } 3937 } 3938 } 3939 3940 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 3941 Intent intent, int userId) { 3942 long identityToken = clearCallingIdentity(); 3943 try { 3944 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3945 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 3946 } 3947 3948 if (intent.getComponent() != null && 3949 GrantCredentialsPermissionActivity.class.getName().equals( 3950 intent.getComponent().getClassName())) { 3951 createNoCredentialsPermissionNotification(account, intent, userId); 3952 } else { 3953 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 3954 intent.addCategory(String.valueOf(notificationId)); 3955 UserHandle user = new UserHandle(userId); 3956 Context contextForUser = getContextForUser(user); 3957 final String notificationTitleFormat = 3958 contextForUser.getText(R.string.notification_title).toString(); 3959 Notification n = new Notification.Builder(contextForUser) 3960 .setWhen(0) 3961 .setSmallIcon(android.R.drawable.stat_sys_warning) 3962 .setColor(contextForUser.getColor( 3963 com.android.internal.R.color.system_notification_accent_color)) 3964 .setContentTitle(String.format(notificationTitleFormat, account.name)) 3965 .setContentText(message) 3966 .setContentIntent(PendingIntent.getActivityAsUser( 3967 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 3968 null, user)) 3969 .build(); 3970 installNotification(notificationId, n, user); 3971 } 3972 } finally { 3973 restoreCallingIdentity(identityToken); 3974 } 3975 } 3976 3977 protected void installNotification(final int notificationId, final Notification n, 3978 UserHandle user) { 3979 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 3980 .notifyAsUser(null, notificationId, n, user); 3981 } 3982 3983 protected void cancelNotification(int id, UserHandle user) { 3984 long identityToken = clearCallingIdentity(); 3985 try { 3986 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 3987 .cancelAsUser(null, id, user); 3988 } finally { 3989 restoreCallingIdentity(identityToken); 3990 } 3991 } 3992 3993 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { 3994 for (String perm : permissions) { 3995 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 3996 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3997 Log.v(TAG, " caller uid " + callingUid + " has " + perm); 3998 } 3999 final int opCode = AppOpsManager.permissionToOpCode(perm); 4000 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( 4001 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { 4002 return true; 4003 } 4004 } 4005 } 4006 return false; 4007 } 4008 4009 private int handleIncomingUser(int userId) { 4010 try { 4011 return ActivityManagerNative.getDefault().handleIncomingUser( 4012 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 4013 } catch (RemoteException re) { 4014 // Shouldn't happen, local. 4015 } 4016 return userId; 4017 } 4018 4019 private boolean isPrivileged(int callingUid) { 4020 final int callingUserId = UserHandle.getUserId(callingUid); 4021 4022 final PackageManager userPackageManager; 4023 try { 4024 userPackageManager = mContext.createPackageContextAsUser( 4025 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 4026 } catch (NameNotFoundException e) { 4027 return false; 4028 } 4029 4030 String[] packages = userPackageManager.getPackagesForUid(callingUid); 4031 for (String name : packages) { 4032 try { 4033 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 4034 if (packageInfo != null 4035 && (packageInfo.applicationInfo.privateFlags 4036 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 4037 return true; 4038 } 4039 } catch (PackageManager.NameNotFoundException e) { 4040 return false; 4041 } 4042 } 4043 return false; 4044 } 4045 4046 private boolean permissionIsGranted( 4047 Account account, String authTokenType, int callerUid, int userId) { 4048 final boolean isPrivileged = isPrivileged(callerUid); 4049 final boolean fromAuthenticator = account != null 4050 && isAccountManagedByCaller(account.type, callerUid, userId); 4051 final boolean hasExplicitGrants = account != null 4052 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); 4053 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4054 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " 4055 + callerUid + ", " + account 4056 + ": is authenticator? " + fromAuthenticator 4057 + ", has explicit permission? " + hasExplicitGrants); 4058 } 4059 return fromAuthenticator || hasExplicitGrants || isPrivileged; 4060 } 4061 4062 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, 4063 String opPackageName) { 4064 if (accountType == null) { 4065 return false; 4066 } else { 4067 return getTypesVisibleToCaller(callingUid, userId, 4068 opPackageName).contains(accountType); 4069 } 4070 } 4071 4072 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { 4073 if (accountType == null) { 4074 return false; 4075 } else { 4076 return getTypesManagedByCaller(callingUid, userId).contains(accountType); 4077 } 4078 } 4079 4080 private List<String> getTypesVisibleToCaller(int callingUid, int userId, 4081 String opPackageName) { 4082 boolean isPermitted = 4083 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, 4084 Manifest.permission.GET_ACCOUNTS_PRIVILEGED); 4085 return getTypesForCaller(callingUid, userId, isPermitted); 4086 } 4087 4088 private List<String> getTypesManagedByCaller(int callingUid, int userId) { 4089 return getTypesForCaller(callingUid, userId, false); 4090 } 4091 4092 private List<String> getTypesForCaller( 4093 int callingUid, int userId, boolean isOtherwisePermitted) { 4094 List<String> managedAccountTypes = new ArrayList<>(); 4095 long identityToken = Binder.clearCallingIdentity(); 4096 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos; 4097 try { 4098 serviceInfos = mAuthenticatorCache.getAllServices(userId); 4099 } finally { 4100 Binder.restoreCallingIdentity(identityToken); 4101 } 4102 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 4103 serviceInfos) { 4104 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); 4105 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { 4106 managedAccountTypes.add(serviceInfo.type.type); 4107 } 4108 } 4109 return managedAccountTypes; 4110 } 4111 4112 private boolean isAccountPresentForCaller(String accountName, String accountType) { 4113 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) { 4114 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) { 4115 if (account.name.equals(accountName)) { 4116 return true; 4117 } 4118 } 4119 } 4120 return false; 4121 } 4122 4123 private static void checkManageUsersPermission(String message) { 4124 if (ActivityManager.checkComponentPermission( 4125 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true) 4126 != PackageManager.PERMISSION_GRANTED) { 4127 throw new SecurityException("You need MANAGE_USERS permission to: " + message); 4128 } 4129 } 4130 4131 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 4132 int callerUid) { 4133 if (callerUid == Process.SYSTEM_UID) { 4134 return true; 4135 } 4136 UserAccounts accounts = getUserAccountsForCaller(); 4137 synchronized (accounts.cacheLock) { 4138 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 4139 String[] args = { String.valueOf(callerUid), authTokenType, 4140 account.name, account.type}; 4141 final boolean permissionGranted = 4142 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; 4143 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 4144 // TODO: Skip this check when running automated tests. Replace this 4145 // with a more general solution. 4146 Log.d(TAG, "no credentials permission for usage of " + account + ", " 4147 + authTokenType + " by uid " + callerUid 4148 + " but ignoring since device is in test harness."); 4149 return true; 4150 } 4151 return permissionGranted; 4152 } 4153 } 4154 4155 private boolean isSystemUid(int callingUid) { 4156 String[] packages = null; 4157 long ident = Binder.clearCallingIdentity(); 4158 try { 4159 packages = mPackageManager.getPackagesForUid(callingUid); 4160 } finally { 4161 Binder.restoreCallingIdentity(ident); 4162 } 4163 if (packages != null) { 4164 for (String name : packages) { 4165 try { 4166 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); 4167 if (packageInfo != null 4168 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 4169 != 0) { 4170 return true; 4171 } 4172 } catch (PackageManager.NameNotFoundException e) { 4173 Log.w(TAG, String.format("Could not find package [%s]", name), e); 4174 } 4175 } 4176 } else { 4177 Log.w(TAG, "No known packages with uid " + callingUid); 4178 } 4179 return false; 4180 } 4181 4182 /** Succeeds if any of the specified permissions are granted. */ 4183 private void checkReadAccountsPermitted( 4184 int callingUid, 4185 String accountType, 4186 int userId, 4187 String opPackageName) { 4188 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { 4189 String msg = String.format( 4190 "caller uid %s cannot access %s accounts", 4191 callingUid, 4192 accountType); 4193 Log.w(TAG, " " + msg); 4194 throw new SecurityException(msg); 4195 } 4196 } 4197 4198 private boolean canUserModifyAccounts(int userId) { 4199 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 4200 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 4201 return false; 4202 } 4203 return true; 4204 } 4205 4206 private boolean canUserModifyAccountsForType(int userId, String accountType) { 4207 DevicePolicyManager dpm = (DevicePolicyManager) mContext 4208 .getSystemService(Context.DEVICE_POLICY_SERVICE); 4209 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 4210 if (typesArray == null) { 4211 return true; 4212 } 4213 for (String forbiddenType : typesArray) { 4214 if (forbiddenType.equals(accountType)) { 4215 return false; 4216 } 4217 } 4218 return true; 4219 } 4220 4221 @Override 4222 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 4223 throws RemoteException { 4224 final int callingUid = getCallingUid(); 4225 4226 if (callingUid != Process.SYSTEM_UID) { 4227 throw new SecurityException(); 4228 } 4229 4230 if (value) { 4231 grantAppPermission(account, authTokenType, uid); 4232 } else { 4233 revokeAppPermission(account, authTokenType, uid); 4234 } 4235 } 4236 4237 /** 4238 * Allow callers with the given uid permission to get credentials for account/authTokenType. 4239 * <p> 4240 * Although this is public it can only be accessed via the AccountManagerService object 4241 * which is in the system. This means we don't need to protect it with permissions. 4242 * @hide 4243 */ 4244 private void grantAppPermission(Account account, String authTokenType, int uid) { 4245 if (account == null || authTokenType == null) { 4246 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 4247 return; 4248 } 4249 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 4250 synchronized (accounts.cacheLock) { 4251 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4252 db.beginTransaction(); 4253 try { 4254 long accountId = getAccountIdLocked(db, account); 4255 if (accountId >= 0) { 4256 ContentValues values = new ContentValues(); 4257 values.put(GRANTS_ACCOUNTS_ID, accountId); 4258 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 4259 values.put(GRANTS_GRANTEE_UID, uid); 4260 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 4261 db.setTransactionSuccessful(); 4262 } 4263 } finally { 4264 db.endTransaction(); 4265 } 4266 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 4267 new UserHandle(accounts.userId)); 4268 } 4269 } 4270 4271 /** 4272 * Don't allow callers with the given uid permission to get credentials for 4273 * account/authTokenType. 4274 * <p> 4275 * Although this is public it can only be accessed via the AccountManagerService object 4276 * which is in the system. This means we don't need to protect it with permissions. 4277 * @hide 4278 */ 4279 private void revokeAppPermission(Account account, String authTokenType, int uid) { 4280 if (account == null || authTokenType == null) { 4281 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 4282 return; 4283 } 4284 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 4285 synchronized (accounts.cacheLock) { 4286 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 4287 db.beginTransaction(); 4288 try { 4289 long accountId = getAccountIdLocked(db, account); 4290 if (accountId >= 0) { 4291 db.delete(TABLE_GRANTS, 4292 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 4293 + GRANTS_GRANTEE_UID + "=?", 4294 new String[]{String.valueOf(accountId), authTokenType, 4295 String.valueOf(uid)}); 4296 db.setTransactionSuccessful(); 4297 } 4298 } finally { 4299 db.endTransaction(); 4300 } 4301 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 4302 new UserHandle(accounts.userId)); 4303 } 4304 } 4305 4306 static final private String stringArrayToString(String[] value) { 4307 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 4308 } 4309 4310 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 4311 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 4312 if (oldAccountsForType != null) { 4313 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 4314 for (Account curAccount : oldAccountsForType) { 4315 if (!curAccount.equals(account)) { 4316 newAccountsList.add(curAccount); 4317 } 4318 } 4319 if (newAccountsList.isEmpty()) { 4320 accounts.accountCache.remove(account.type); 4321 } else { 4322 Account[] newAccountsForType = new Account[newAccountsList.size()]; 4323 newAccountsForType = newAccountsList.toArray(newAccountsForType); 4324 accounts.accountCache.put(account.type, newAccountsForType); 4325 } 4326 } 4327 accounts.userDataCache.remove(account); 4328 accounts.authTokenCache.remove(account); 4329 accounts.previousNameCache.remove(account); 4330 } 4331 4332 /** 4333 * This assumes that the caller has already checked that the account is not already present. 4334 */ 4335 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 4336 Account[] accountsForType = accounts.accountCache.get(account.type); 4337 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 4338 Account[] newAccountsForType = new Account[oldLength + 1]; 4339 if (accountsForType != null) { 4340 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 4341 } 4342 newAccountsForType[oldLength] = account; 4343 accounts.accountCache.put(account.type, newAccountsForType); 4344 } 4345 4346 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 4347 int callingUid, String callingPackage) { 4348 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 4349 || callingUid == Process.myUid()) { 4350 return unfiltered; 4351 } 4352 UserInfo user = mUserManager.getUserInfo(userAccounts.userId); 4353 if (user != null && user.isRestricted()) { 4354 String[] packages = mPackageManager.getPackagesForUid(callingUid); 4355 // If any of the packages is a white listed package, return the full set, 4356 // otherwise return non-shared accounts only. 4357 // This might be a temporary way to specify a whitelist 4358 String whiteList = mContext.getResources().getString( 4359 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 4360 for (String packageName : packages) { 4361 if (whiteList.contains(";" + packageName + ";")) { 4362 return unfiltered; 4363 } 4364 } 4365 ArrayList<Account> allowed = new ArrayList<Account>(); 4366 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 4367 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 4368 String requiredAccountType = ""; 4369 try { 4370 // If there's an explicit callingPackage specified, check if that package 4371 // opted in to see restricted accounts. 4372 if (callingPackage != null) { 4373 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 4374 if (pi != null && pi.restrictedAccountType != null) { 4375 requiredAccountType = pi.restrictedAccountType; 4376 } 4377 } else { 4378 // Otherwise check if the callingUid has a package that has opted in 4379 for (String packageName : packages) { 4380 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 4381 if (pi != null && pi.restrictedAccountType != null) { 4382 requiredAccountType = pi.restrictedAccountType; 4383 break; 4384 } 4385 } 4386 } 4387 } catch (NameNotFoundException nnfe) { 4388 } 4389 for (Account account : unfiltered) { 4390 if (account.type.equals(requiredAccountType)) { 4391 allowed.add(account); 4392 } else { 4393 boolean found = false; 4394 for (Account shared : sharedAccounts) { 4395 if (shared.equals(account)) { 4396 found = true; 4397 break; 4398 } 4399 } 4400 if (!found) { 4401 allowed.add(account); 4402 } 4403 } 4404 } 4405 Account[] filtered = new Account[allowed.size()]; 4406 allowed.toArray(filtered); 4407 return filtered; 4408 } else { 4409 return unfiltered; 4410 } 4411 } 4412 4413 /* 4414 * packageName can be null. If not null, it should be used to filter out restricted accounts 4415 * that the package is not allowed to access. 4416 */ 4417 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 4418 int callingUid, String callingPackage) { 4419 if (accountType != null) { 4420 final Account[] accounts = userAccounts.accountCache.get(accountType); 4421 if (accounts == null) { 4422 return EMPTY_ACCOUNT_ARRAY; 4423 } else { 4424 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 4425 callingUid, callingPackage); 4426 } 4427 } else { 4428 int totalLength = 0; 4429 for (Account[] accounts : userAccounts.accountCache.values()) { 4430 totalLength += accounts.length; 4431 } 4432 if (totalLength == 0) { 4433 return EMPTY_ACCOUNT_ARRAY; 4434 } 4435 Account[] accounts = new Account[totalLength]; 4436 totalLength = 0; 4437 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 4438 System.arraycopy(accountsOfType, 0, accounts, totalLength, 4439 accountsOfType.length); 4440 totalLength += accountsOfType.length; 4441 } 4442 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 4443 } 4444 } 4445 4446 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 4447 Account account, String key, String value) { 4448 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 4449 if (userDataForAccount == null) { 4450 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 4451 accounts.userDataCache.put(account, userDataForAccount); 4452 } 4453 if (value == null) { 4454 userDataForAccount.remove(key); 4455 } else { 4456 userDataForAccount.put(key, value); 4457 } 4458 } 4459 4460 protected String readCachedTokenInternal( 4461 UserAccounts accounts, 4462 Account account, 4463 String tokenType, 4464 String callingPackage, 4465 byte[] pkgSigDigest) { 4466 synchronized (accounts.cacheLock) { 4467 return accounts.accountTokenCaches.get( 4468 account, tokenType, callingPackage, pkgSigDigest); 4469 } 4470 } 4471 4472 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 4473 Account account, String key, String value) { 4474 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 4475 if (authTokensForAccount == null) { 4476 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 4477 accounts.authTokenCache.put(account, authTokensForAccount); 4478 } 4479 if (value == null) { 4480 authTokensForAccount.remove(key); 4481 } else { 4482 authTokensForAccount.put(key, value); 4483 } 4484 } 4485 4486 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 4487 String authTokenType) { 4488 synchronized (accounts.cacheLock) { 4489 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 4490 if (authTokensForAccount == null) { 4491 // need to populate the cache for this account 4492 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 4493 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 4494 accounts.authTokenCache.put(account, authTokensForAccount); 4495 } 4496 return authTokensForAccount.get(authTokenType); 4497 } 4498 } 4499 4500 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { 4501 synchronized (accounts.cacheLock) { 4502 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 4503 if (userDataForAccount == null) { 4504 // need to populate the cache for this account 4505 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 4506 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 4507 accounts.userDataCache.put(account, userDataForAccount); 4508 } 4509 return userDataForAccount.get(key); 4510 } 4511 } 4512 4513 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 4514 final SQLiteDatabase db, Account account) { 4515 HashMap<String, String> userDataForAccount = new HashMap<String, String>(); 4516 Cursor cursor = db.query(TABLE_EXTRAS, 4517 COLUMNS_EXTRAS_KEY_AND_VALUE, 4518 SELECTION_USERDATA_BY_ACCOUNT, 4519 new String[]{account.name, account.type}, 4520 null, null, null); 4521 try { 4522 while (cursor.moveToNext()) { 4523 final String tmpkey = cursor.getString(0); 4524 final String value = cursor.getString(1); 4525 userDataForAccount.put(tmpkey, value); 4526 } 4527 } finally { 4528 cursor.close(); 4529 } 4530 return userDataForAccount; 4531 } 4532 4533 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 4534 final SQLiteDatabase db, Account account) { 4535 HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); 4536 Cursor cursor = db.query(TABLE_AUTHTOKENS, 4537 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 4538 SELECTION_AUTHTOKENS_BY_ACCOUNT, 4539 new String[]{account.name, account.type}, 4540 null, null, null); 4541 try { 4542 while (cursor.moveToNext()) { 4543 final String type = cursor.getString(0); 4544 final String authToken = cursor.getString(1); 4545 authTokensForAccount.put(type, authToken); 4546 } 4547 } finally { 4548 cursor.close(); 4549 } 4550 return authTokensForAccount; 4551 } 4552 4553 private Context getContextForUser(UserHandle user) { 4554 try { 4555 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 4556 } catch (NameNotFoundException e) { 4557 // Default to mContext, not finding the package system is running as is unlikely. 4558 return mContext; 4559 } 4560 } 4561 4562 private void sendResponse(IAccountManagerResponse response, Bundle result) { 4563 try { 4564 response.onResult(result); 4565 } catch (RemoteException e) { 4566 // if the caller is dead then there is no one to care about remote 4567 // exceptions 4568 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4569 Log.v(TAG, "failure while notifying response", e); 4570 } 4571 } 4572 } 4573 4574 private void sendErrorResponse(IAccountManagerResponse response, int errorCode, 4575 String errorMessage) { 4576 try { 4577 response.onError(errorCode, errorMessage); 4578 } catch (RemoteException e) { 4579 // if the caller is dead then there is no one to care about remote 4580 // exceptions 4581 if (Log.isLoggable(TAG, Log.VERBOSE)) { 4582 Log.v(TAG, "failure while notifying response", e); 4583 } 4584 } 4585 } 4586} 4587