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