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