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