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