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