AccountManagerService.java revision 23f58260bd70dc9ca2787f54093ec0369dc3f02c
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accounts; 18 19import android.Manifest; 20import android.accounts.Account; 21import android.accounts.AccountAndUser; 22import android.accounts.AccountAuthenticatorResponse; 23import android.accounts.AccountManager; 24import android.accounts.AuthenticatorDescription; 25import android.accounts.CantAddAccountActivity; 26import android.accounts.GrantCredentialsPermissionActivity; 27import android.accounts.IAccountAuthenticator; 28import android.accounts.IAccountAuthenticatorResponse; 29import android.accounts.IAccountManager; 30import android.accounts.IAccountManagerResponse; 31import android.app.ActivityManager; 32import android.app.ActivityManagerNative; 33import android.app.AppGlobals; 34import android.app.Notification; 35import android.app.NotificationManager; 36import android.app.PendingIntent; 37import android.app.admin.DevicePolicyManager; 38import android.content.BroadcastReceiver; 39import android.content.ComponentName; 40import android.content.ContentValues; 41import android.content.Context; 42import android.content.Intent; 43import android.content.IntentFilter; 44import android.content.ServiceConnection; 45import android.content.pm.ApplicationInfo; 46import android.content.pm.PackageInfo; 47import android.content.pm.PackageManager; 48import android.content.pm.PackageManager.NameNotFoundException; 49import android.content.pm.RegisteredServicesCache; 50import android.content.pm.RegisteredServicesCacheListener; 51import android.content.pm.ResolveInfo; 52import android.content.pm.UserInfo; 53import android.database.Cursor; 54import android.database.DatabaseUtils; 55import android.database.sqlite.SQLiteDatabase; 56import android.database.sqlite.SQLiteOpenHelper; 57import android.os.Binder; 58import android.os.Bundle; 59import android.os.Environment; 60import android.os.Handler; 61import android.os.IBinder; 62import android.os.Looper; 63import android.os.Message; 64import android.os.Parcel; 65import android.os.Process; 66import android.os.RemoteException; 67import android.os.SystemClock; 68import android.os.UserHandle; 69import android.os.UserManager; 70import android.text.TextUtils; 71import android.util.Log; 72import android.util.Pair; 73import android.util.Slog; 74import android.util.SparseArray; 75 76import com.android.internal.R; 77import com.android.internal.util.ArrayUtils; 78import com.android.internal.util.IndentingPrintWriter; 79import com.android.server.FgThread; 80 81import com.google.android.collect.Lists; 82import com.google.android.collect.Sets; 83 84import java.io.File; 85import java.io.FileDescriptor; 86import java.io.PrintWriter; 87import java.util.ArrayList; 88import java.util.Arrays; 89import java.util.Collection; 90import java.util.HashMap; 91import java.util.HashSet; 92import java.util.LinkedHashMap; 93import java.util.List; 94import java.util.Map; 95import java.util.concurrent.atomic.AtomicInteger; 96import java.util.concurrent.atomic.AtomicReference; 97 98/** 99 * A system service that provides account, password, and authtoken management for all 100 * accounts on the device. Some of these calls are implemented with the help of the corresponding 101 * {@link IAccountAuthenticator} services. This service is not accessed by users directly, 102 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: 103 * AccountManager accountManager = AccountManager.get(context); 104 * @hide 105 */ 106public class AccountManagerService 107 extends IAccountManager.Stub 108 implements RegisteredServicesCacheListener<AuthenticatorDescription> { 109 private static final String TAG = "AccountManagerService"; 110 111 private static final int TIMEOUT_DELAY_MS = 1000 * 60; 112 private static final String DATABASE_NAME = "accounts.db"; 113 private static final int DATABASE_VERSION = 6; 114 115 private final Context mContext; 116 117 private final PackageManager mPackageManager; 118 private UserManager mUserManager; 119 120 private final MessageHandler mMessageHandler; 121 122 // Messages that can be sent on mHandler 123 private static final int MESSAGE_TIMED_OUT = 3; 124 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; 125 126 private final IAccountAuthenticatorCache mAuthenticatorCache; 127 128 private static final String TABLE_ACCOUNTS = "accounts"; 129 private static final String ACCOUNTS_ID = "_id"; 130 private static final String ACCOUNTS_NAME = "name"; 131 private static final String ACCOUNTS_TYPE = "type"; 132 private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; 133 private static final String ACCOUNTS_PASSWORD = "password"; 134 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name"; 135 136 private static final String TABLE_AUTHTOKENS = "authtokens"; 137 private static final String AUTHTOKENS_ID = "_id"; 138 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; 139 private static final String AUTHTOKENS_TYPE = "type"; 140 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; 141 142 private static final String TABLE_GRANTS = "grants"; 143 private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; 144 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; 145 private static final String GRANTS_GRANTEE_UID = "uid"; 146 147 private static final String TABLE_EXTRAS = "extras"; 148 private static final String EXTRAS_ID = "_id"; 149 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; 150 private static final String EXTRAS_KEY = "key"; 151 private static final String EXTRAS_VALUE = "value"; 152 153 private static final String TABLE_META = "meta"; 154 private static final String META_KEY = "key"; 155 private static final String META_VALUE = "value"; 156 157 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts"; 158 159 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = 160 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; 161 private static final Intent ACCOUNTS_CHANGED_INTENT; 162 163 private static final String COUNT_OF_MATCHING_GRANTS = "" 164 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 165 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 166 + " AND " + GRANTS_GRANTEE_UID + "=?" 167 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" 168 + " AND " + ACCOUNTS_NAME + "=?" 169 + " AND " + ACCOUNTS_TYPE + "=?"; 170 171 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = 172 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 173 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, 174 AUTHTOKENS_AUTHTOKEN}; 175 176 private static final String SELECTION_USERDATA_BY_ACCOUNT = 177 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 178 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; 179 180 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); 181 private final AtomicInteger mNotificationIds = new AtomicInteger(1); 182 183 static class UserAccounts { 184 private final int userId; 185 private final DatabaseHelper openHelper; 186 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> 187 credentialsPermissionNotificationIds = 188 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); 189 private final HashMap<Account, Integer> signinRequiredNotificationIds = 190 new HashMap<Account, Integer>(); 191 private final Object cacheLock = new Object(); 192 /** protected by the {@link #cacheLock} */ 193 private final HashMap<String, Account[]> accountCache = 194 new LinkedHashMap<String, Account[]>(); 195 /** protected by the {@link #cacheLock} */ 196 private final HashMap<Account, HashMap<String, String>> userDataCache = 197 new HashMap<Account, HashMap<String, String>>(); 198 /** protected by the {@link #cacheLock} */ 199 private final HashMap<Account, HashMap<String, String>> authTokenCache = 200 new HashMap<Account, HashMap<String, String>>(); 201 /** 202 * protected by the {@link #cacheLock} 203 * 204 * Caches the previous names associated with an account. Previous names 205 * should be cached because we expect that when an Account is renamed, 206 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and 207 * want to know if the accounts they care about have been renamed. 208 * 209 * The previous names are wrapped in an {@link AtomicReference} so that 210 * we can distinguish between those accounts with no previous names and 211 * those whose previous names haven't been cached (yet). 212 */ 213 private final HashMap<Account, AtomicReference<String>> previousNameCache = 214 new HashMap<Account, AtomicReference<String>>(); 215 216 UserAccounts(Context context, int userId) { 217 this.userId = userId; 218 synchronized (cacheLock) { 219 openHelper = new DatabaseHelper(context, userId); 220 } 221 } 222 } 223 224 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>(); 225 226 private static AtomicReference<AccountManagerService> sThis = 227 new AtomicReference<AccountManagerService>(); 228 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; 229 230 static { 231 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); 232 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 233 } 234 235 236 /** 237 * This should only be called by system code. One should only call this after the service 238 * has started. 239 * @return a reference to the AccountManagerService instance 240 * @hide 241 */ 242 public static AccountManagerService getSingleton() { 243 return sThis.get(); 244 } 245 246 public AccountManagerService(Context context) { 247 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); 248 } 249 250 public AccountManagerService(Context context, PackageManager packageManager, 251 IAccountAuthenticatorCache authenticatorCache) { 252 mContext = context; 253 mPackageManager = packageManager; 254 255 mMessageHandler = new MessageHandler(FgThread.get().getLooper()); 256 257 mAuthenticatorCache = authenticatorCache; 258 mAuthenticatorCache.setListener(this, null /* Handler */); 259 260 sThis.set(this); 261 262 IntentFilter intentFilter = new IntentFilter(); 263 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 264 intentFilter.addDataScheme("package"); 265 mContext.registerReceiver(new BroadcastReceiver() { 266 @Override 267 public void onReceive(Context context1, Intent intent) { 268 // 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); 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 return; 1801 } 1802 1803 UserAccounts accounts = getUserAccountsForCaller(); 1804 final int pid = Binder.getCallingPid(); 1805 final int uid = Binder.getCallingUid(); 1806 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 1807 options.putInt(AccountManager.KEY_CALLER_UID, uid); 1808 options.putInt(AccountManager.KEY_CALLER_PID, pid); 1809 1810 long identityToken = clearCallingIdentity(); 1811 try { 1812 new Session(accounts, response, accountType, expectActivityLaunch, 1813 true /* stripAuthTokenFromResult */) { 1814 @Override 1815 public void run() throws RemoteException { 1816 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 1817 options); 1818 } 1819 1820 @Override 1821 protected String toDebugString(long now) { 1822 return super.toDebugString(now) + ", addAccount" 1823 + ", accountType " + accountType 1824 + ", requiredFeatures " 1825 + (requiredFeatures != null 1826 ? TextUtils.join(",", requiredFeatures) 1827 : null); 1828 } 1829 }.bind(); 1830 } finally { 1831 restoreCallingIdentity(identityToken); 1832 } 1833 } 1834 1835 @Override 1836 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 1837 final String authTokenType, final String[] requiredFeatures, 1838 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 1839 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1840 Log.v(TAG, "addAccount: accountType " + accountType 1841 + ", response " + response 1842 + ", authTokenType " + authTokenType 1843 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 1844 + ", expectActivityLaunch " + expectActivityLaunch 1845 + ", caller's uid " + Binder.getCallingUid() 1846 + ", pid " + Binder.getCallingPid() 1847 + ", for user id " + userId); 1848 } 1849 if (response == null) throw new IllegalArgumentException("response is null"); 1850 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1851 checkManageAccountsPermission(); 1852 1853 // Only allow the system process to add accounts of other users 1854 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 1855 + " trying to add account for " + userId); 1856 1857 // Is user disallowed from modifying accounts? 1858 if (!canUserModifyAccounts(userId)) { 1859 try { 1860 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1861 "User is not allowed to add an account!"); 1862 } catch (RemoteException re) { 1863 } 1864 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED); 1865 return; 1866 } 1867 if (!canUserModifyAccountsForType(userId, accountType)) { 1868 try { 1869 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1870 "User cannot modify accounts of this type (policy)."); 1871 } catch (RemoteException re) { 1872 } 1873 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE); 1874 return; 1875 } 1876 1877 UserAccounts accounts = getUserAccounts(userId); 1878 final int pid = Binder.getCallingPid(); 1879 final int uid = Binder.getCallingUid(); 1880 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 1881 options.putInt(AccountManager.KEY_CALLER_UID, uid); 1882 options.putInt(AccountManager.KEY_CALLER_PID, pid); 1883 1884 long identityToken = clearCallingIdentity(); 1885 try { 1886 new Session(accounts, response, accountType, expectActivityLaunch, 1887 true /* stripAuthTokenFromResult */) { 1888 @Override 1889 public void run() throws RemoteException { 1890 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 1891 options); 1892 } 1893 1894 @Override 1895 protected String toDebugString(long now) { 1896 return super.toDebugString(now) + ", addAccount" 1897 + ", accountType " + accountType 1898 + ", requiredFeatures " 1899 + (requiredFeatures != null 1900 ? TextUtils.join(",", requiredFeatures) 1901 : null); 1902 } 1903 }.bind(); 1904 } finally { 1905 restoreCallingIdentity(identityToken); 1906 } 1907 } 1908 1909 private void showCantAddAccount(int errorCode) { 1910 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 1911 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 1912 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1913 long identityToken = clearCallingIdentity(); 1914 try { 1915 mContext.startActivity(cantAddAccount); 1916 } finally { 1917 restoreCallingIdentity(identityToken); 1918 } 1919 } 1920 1921 @Override 1922 public void confirmCredentialsAsUser(IAccountManagerResponse response, 1923 final Account account, final Bundle options, final boolean expectActivityLaunch, 1924 int userId) { 1925 // Only allow the system process to read accounts of other users 1926 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 1927 + " trying to confirm account credentials for " + userId); 1928 1929 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1930 Log.v(TAG, "confirmCredentials: " + account 1931 + ", response " + response 1932 + ", expectActivityLaunch " + expectActivityLaunch 1933 + ", caller's uid " + Binder.getCallingUid() 1934 + ", pid " + Binder.getCallingPid()); 1935 } 1936 if (response == null) throw new IllegalArgumentException("response is null"); 1937 if (account == null) throw new IllegalArgumentException("account is null"); 1938 checkManageAccountsPermission(); 1939 UserAccounts accounts = getUserAccounts(userId); 1940 long identityToken = clearCallingIdentity(); 1941 try { 1942 new Session(accounts, response, account.type, expectActivityLaunch, 1943 true /* stripAuthTokenFromResult */) { 1944 @Override 1945 public void run() throws RemoteException { 1946 mAuthenticator.confirmCredentials(this, account, options); 1947 } 1948 @Override 1949 protected String toDebugString(long now) { 1950 return super.toDebugString(now) + ", confirmCredentials" 1951 + ", " + account; 1952 } 1953 }.bind(); 1954 } finally { 1955 restoreCallingIdentity(identityToken); 1956 } 1957 } 1958 1959 @Override 1960 public void updateCredentials(IAccountManagerResponse response, final Account account, 1961 final String authTokenType, final boolean expectActivityLaunch, 1962 final Bundle loginOptions) { 1963 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1964 Log.v(TAG, "updateCredentials: " + account 1965 + ", response " + response 1966 + ", authTokenType " + authTokenType 1967 + ", expectActivityLaunch " + expectActivityLaunch 1968 + ", caller's uid " + Binder.getCallingUid() 1969 + ", pid " + Binder.getCallingPid()); 1970 } 1971 if (response == null) throw new IllegalArgumentException("response is null"); 1972 if (account == null) throw new IllegalArgumentException("account is null"); 1973 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1974 checkManageAccountsPermission(); 1975 UserAccounts accounts = getUserAccountsForCaller(); 1976 long identityToken = clearCallingIdentity(); 1977 try { 1978 new Session(accounts, response, account.type, expectActivityLaunch, 1979 true /* stripAuthTokenFromResult */) { 1980 @Override 1981 public void run() throws RemoteException { 1982 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 1983 } 1984 @Override 1985 protected String toDebugString(long now) { 1986 if (loginOptions != null) loginOptions.keySet(); 1987 return super.toDebugString(now) + ", updateCredentials" 1988 + ", " + account 1989 + ", authTokenType " + authTokenType 1990 + ", loginOptions " + loginOptions; 1991 } 1992 }.bind(); 1993 } finally { 1994 restoreCallingIdentity(identityToken); 1995 } 1996 } 1997 1998 @Override 1999 public void editProperties(IAccountManagerResponse response, final String accountType, 2000 final boolean expectActivityLaunch) { 2001 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2002 Log.v(TAG, "editProperties: accountType " + accountType 2003 + ", response " + response 2004 + ", expectActivityLaunch " + expectActivityLaunch 2005 + ", caller's uid " + Binder.getCallingUid() 2006 + ", pid " + Binder.getCallingPid()); 2007 } 2008 if (response == null) throw new IllegalArgumentException("response is null"); 2009 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2010 checkManageAccountsPermission(); 2011 UserAccounts accounts = getUserAccountsForCaller(); 2012 long identityToken = clearCallingIdentity(); 2013 try { 2014 new Session(accounts, response, accountType, expectActivityLaunch, 2015 true /* stripAuthTokenFromResult */) { 2016 @Override 2017 public void run() throws RemoteException { 2018 mAuthenticator.editProperties(this, mAccountType); 2019 } 2020 @Override 2021 protected String toDebugString(long now) { 2022 return super.toDebugString(now) + ", editProperties" 2023 + ", accountType " + accountType; 2024 } 2025 }.bind(); 2026 } finally { 2027 restoreCallingIdentity(identityToken); 2028 } 2029 } 2030 2031 private class GetAccountsByTypeAndFeatureSession extends Session { 2032 private final String[] mFeatures; 2033 private volatile Account[] mAccountsOfType = null; 2034 private volatile ArrayList<Account> mAccountsWithFeatures = null; 2035 private volatile int mCurrentAccount = 0; 2036 private final int mCallingUid; 2037 2038 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 2039 IAccountManagerResponse response, String type, String[] features, int callingUid) { 2040 super(accounts, response, type, false /* expectActivityLaunch */, 2041 true /* stripAuthTokenFromResult */); 2042 mCallingUid = callingUid; 2043 mFeatures = features; 2044 } 2045 2046 @Override 2047 public void run() throws RemoteException { 2048 synchronized (mAccounts.cacheLock) { 2049 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 2050 null); 2051 } 2052 // check whether each account matches the requested features 2053 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 2054 mCurrentAccount = 0; 2055 2056 checkAccount(); 2057 } 2058 2059 public void checkAccount() { 2060 if (mCurrentAccount >= mAccountsOfType.length) { 2061 sendResult(); 2062 return; 2063 } 2064 2065 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 2066 if (accountAuthenticator == null) { 2067 // It is possible that the authenticator has died, which is indicated by 2068 // mAuthenticator being set to null. If this happens then just abort. 2069 // There is no need to send back a result or error in this case since 2070 // that already happened when mAuthenticator was cleared. 2071 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2072 Log.v(TAG, "checkAccount: aborting session since we are no longer" 2073 + " connected to the authenticator, " + toDebugString()); 2074 } 2075 return; 2076 } 2077 try { 2078 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 2079 } catch (RemoteException e) { 2080 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 2081 } 2082 } 2083 2084 @Override 2085 public void onResult(Bundle result) { 2086 mNumResults++; 2087 if (result == null) { 2088 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 2089 return; 2090 } 2091 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 2092 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 2093 } 2094 mCurrentAccount++; 2095 checkAccount(); 2096 } 2097 2098 public void sendResult() { 2099 IAccountManagerResponse response = getResponseAndClose(); 2100 if (response != null) { 2101 try { 2102 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 2103 for (int i = 0; i < accounts.length; i++) { 2104 accounts[i] = mAccountsWithFeatures.get(i); 2105 } 2106 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2107 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2108 + response); 2109 } 2110 Bundle result = new Bundle(); 2111 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2112 response.onResult(result); 2113 } catch (RemoteException e) { 2114 // if the caller is dead then there is no one to care about remote exceptions 2115 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2116 Log.v(TAG, "failure while notifying response", e); 2117 } 2118 } 2119 } 2120 } 2121 2122 2123 @Override 2124 protected String toDebugString(long now) { 2125 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 2126 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 2127 } 2128 } 2129 2130 /** 2131 * Returns the accounts for a specific user 2132 * @hide 2133 */ 2134 public Account[] getAccounts(int userId) { 2135 checkReadAccountsPermission(); 2136 UserAccounts accounts = getUserAccounts(userId); 2137 int callingUid = Binder.getCallingUid(); 2138 long identityToken = clearCallingIdentity(); 2139 try { 2140 synchronized (accounts.cacheLock) { 2141 return getAccountsFromCacheLocked(accounts, null, callingUid, null); 2142 } 2143 } finally { 2144 restoreCallingIdentity(identityToken); 2145 } 2146 } 2147 2148 /** 2149 * Returns accounts for all running users. 2150 * 2151 * @hide 2152 */ 2153 public AccountAndUser[] getRunningAccounts() { 2154 final int[] runningUserIds; 2155 try { 2156 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 2157 } catch (RemoteException e) { 2158 // Running in system_server; should never happen 2159 throw new RuntimeException(e); 2160 } 2161 return getAccounts(runningUserIds); 2162 } 2163 2164 /** {@hide} */ 2165 public AccountAndUser[] getAllAccounts() { 2166 final List<UserInfo> users = getUserManager().getUsers(); 2167 final int[] userIds = new int[users.size()]; 2168 for (int i = 0; i < userIds.length; i++) { 2169 userIds[i] = users.get(i).id; 2170 } 2171 return getAccounts(userIds); 2172 } 2173 2174 private AccountAndUser[] getAccounts(int[] userIds) { 2175 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 2176 for (int userId : userIds) { 2177 UserAccounts userAccounts = getUserAccounts(userId); 2178 if (userAccounts == null) continue; 2179 synchronized (userAccounts.cacheLock) { 2180 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 2181 Binder.getCallingUid(), null); 2182 for (int a = 0; a < accounts.length; a++) { 2183 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 2184 } 2185 } 2186 } 2187 2188 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 2189 return runningAccounts.toArray(accountsArray); 2190 } 2191 2192 @Override 2193 public Account[] getAccountsAsUser(String type, int userId) { 2194 return getAccountsAsUser(type, userId, null, -1); 2195 } 2196 2197 private Account[] getAccountsAsUser(String type, int userId, String callingPackage, 2198 int packageUid) { 2199 int callingUid = Binder.getCallingUid(); 2200 // Only allow the system process to read accounts of other users 2201 if (userId != UserHandle.getCallingUserId() 2202 && callingUid != Process.myUid() 2203 && mContext.checkCallingOrSelfPermission( 2204 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 2205 != PackageManager.PERMISSION_GRANTED) { 2206 throw new SecurityException("User " + UserHandle.getCallingUserId() 2207 + " trying to get account for " + userId); 2208 } 2209 2210 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2211 Log.v(TAG, "getAccounts: accountType " + type 2212 + ", caller's uid " + Binder.getCallingUid() 2213 + ", pid " + Binder.getCallingPid()); 2214 } 2215 // If the original calling app was using the framework account chooser activity, we'll 2216 // be passed in the original caller's uid here, which is what should be used for filtering. 2217 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 2218 callingUid = packageUid; 2219 } 2220 checkReadAccountsPermission(); 2221 UserAccounts accounts = getUserAccounts(userId); 2222 long identityToken = clearCallingIdentity(); 2223 try { 2224 synchronized (accounts.cacheLock) { 2225 return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); 2226 } 2227 } finally { 2228 restoreCallingIdentity(identityToken); 2229 } 2230 } 2231 2232 @Override 2233 public boolean addSharedAccountAsUser(Account account, int userId) { 2234 userId = handleIncomingUser(userId); 2235 SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase(); 2236 ContentValues values = new ContentValues(); 2237 values.put(ACCOUNTS_NAME, account.name); 2238 values.put(ACCOUNTS_TYPE, account.type); 2239 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2240 new String[] {account.name, account.type}); 2241 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 2242 if (accountId < 0) { 2243 Log.w(TAG, "insertAccountIntoDatabase: " + account 2244 + ", skipping the DB insert failed"); 2245 return false; 2246 } 2247 return true; 2248 } 2249 2250 @Override 2251 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 2252 userId = handleIncomingUser(userId); 2253 UserAccounts accounts = getUserAccounts(userId); 2254 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2255 final ContentValues values = new ContentValues(); 2256 values.put(ACCOUNTS_NAME, newName); 2257 values.put(ACCOUNTS_PREVIOUS_NAME, account.name); 2258 int r = db.update( 2259 TABLE_SHARED_ACCOUNTS, 2260 values, 2261 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2262 new String[] { account.name, account.type }); 2263 if (r > 0) { 2264 // Recursively rename the account. 2265 renameAccountInternal(accounts, account, newName); 2266 } 2267 return r > 0; 2268 } 2269 2270 @Override 2271 public boolean removeSharedAccountAsUser(Account account, int userId) { 2272 userId = handleIncomingUser(userId); 2273 UserAccounts accounts = getUserAccounts(userId); 2274 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2275 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2276 new String[] {account.name, account.type}); 2277 if (r > 0) { 2278 removeAccountInternal(accounts, account); 2279 } 2280 return r > 0; 2281 } 2282 2283 @Override 2284 public Account[] getSharedAccountsAsUser(int userId) { 2285 userId = handleIncomingUser(userId); 2286 UserAccounts accounts = getUserAccounts(userId); 2287 ArrayList<Account> accountList = new ArrayList<Account>(); 2288 Cursor cursor = null; 2289 try { 2290 cursor = accounts.openHelper.getReadableDatabase() 2291 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 2292 null, null, null, null, null); 2293 if (cursor != null && cursor.moveToFirst()) { 2294 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 2295 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 2296 do { 2297 accountList.add(new Account(cursor.getString(nameIndex), 2298 cursor.getString(typeIndex))); 2299 } while (cursor.moveToNext()); 2300 } 2301 } finally { 2302 if (cursor != null) { 2303 cursor.close(); 2304 } 2305 } 2306 Account[] accountArray = new Account[accountList.size()]; 2307 accountList.toArray(accountArray); 2308 return accountArray; 2309 } 2310 2311 @Override 2312 public Account[] getAccounts(String type) { 2313 return getAccountsAsUser(type, UserHandle.getCallingUserId()); 2314 } 2315 2316 @Override 2317 public Account[] getAccountsForPackage(String packageName, int uid) { 2318 int callingUid = Binder.getCallingUid(); 2319 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 2320 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 2321 + callingUid + " with uid=" + uid); 2322 } 2323 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid); 2324 } 2325 2326 @Override 2327 public Account[] getAccountsByTypeForPackage(String type, String packageName) { 2328 checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS); 2329 int packageUid = -1; 2330 try { 2331 packageUid = AppGlobals.getPackageManager().getPackageUid( 2332 packageName, UserHandle.getCallingUserId()); 2333 } catch (RemoteException re) { 2334 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 2335 return new Account[0]; 2336 } 2337 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid); 2338 } 2339 2340 @Override 2341 public void getAccountsByFeatures(IAccountManagerResponse response, 2342 String type, String[] features) { 2343 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2344 Log.v(TAG, "getAccounts: accountType " + type 2345 + ", response " + response 2346 + ", features " + stringArrayToString(features) 2347 + ", caller's uid " + Binder.getCallingUid() 2348 + ", pid " + Binder.getCallingPid()); 2349 } 2350 if (response == null) throw new IllegalArgumentException("response is null"); 2351 if (type == null) throw new IllegalArgumentException("accountType is null"); 2352 checkReadAccountsPermission(); 2353 UserAccounts userAccounts = getUserAccountsForCaller(); 2354 int callingUid = Binder.getCallingUid(); 2355 long identityToken = clearCallingIdentity(); 2356 try { 2357 if (features == null || features.length == 0) { 2358 Account[] accounts; 2359 synchronized (userAccounts.cacheLock) { 2360 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 2361 } 2362 Bundle result = new Bundle(); 2363 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2364 onResult(response, result); 2365 return; 2366 } 2367 new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features, 2368 callingUid).bind(); 2369 } finally { 2370 restoreCallingIdentity(identityToken); 2371 } 2372 } 2373 2374 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 2375 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 2376 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 2377 try { 2378 if (cursor.moveToNext()) { 2379 return cursor.getLong(0); 2380 } 2381 return -1; 2382 } finally { 2383 cursor.close(); 2384 } 2385 } 2386 2387 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 2388 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, 2389 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 2390 new String[]{key}, null, null, null); 2391 try { 2392 if (cursor.moveToNext()) { 2393 return cursor.getLong(0); 2394 } 2395 return -1; 2396 } finally { 2397 cursor.close(); 2398 } 2399 } 2400 2401 private abstract class Session extends IAccountAuthenticatorResponse.Stub 2402 implements IBinder.DeathRecipient, ServiceConnection { 2403 IAccountManagerResponse mResponse; 2404 final String mAccountType; 2405 final boolean mExpectActivityLaunch; 2406 final long mCreationTime; 2407 2408 public int mNumResults = 0; 2409 private int mNumRequestContinued = 0; 2410 private int mNumErrors = 0; 2411 2412 IAccountAuthenticator mAuthenticator = null; 2413 2414 private final boolean mStripAuthTokenFromResult; 2415 protected final UserAccounts mAccounts; 2416 2417 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 2418 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) { 2419 super(); 2420 //if (response == null) throw new IllegalArgumentException("response is null"); 2421 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2422 mAccounts = accounts; 2423 mStripAuthTokenFromResult = stripAuthTokenFromResult; 2424 mResponse = response; 2425 mAccountType = accountType; 2426 mExpectActivityLaunch = expectActivityLaunch; 2427 mCreationTime = SystemClock.elapsedRealtime(); 2428 synchronized (mSessions) { 2429 mSessions.put(toString(), this); 2430 } 2431 if (response != null) { 2432 try { 2433 response.asBinder().linkToDeath(this, 0 /* flags */); 2434 } catch (RemoteException e) { 2435 mResponse = null; 2436 binderDied(); 2437 } 2438 } 2439 } 2440 2441 IAccountManagerResponse getResponseAndClose() { 2442 if (mResponse == null) { 2443 // this session has already been closed 2444 return null; 2445 } 2446 IAccountManagerResponse response = mResponse; 2447 close(); // this clears mResponse so we need to save the response before this call 2448 return response; 2449 } 2450 2451 private void close() { 2452 synchronized (mSessions) { 2453 if (mSessions.remove(toString()) == null) { 2454 // the session was already closed, so bail out now 2455 return; 2456 } 2457 } 2458 if (mResponse != null) { 2459 // stop listening for response deaths 2460 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 2461 2462 // clear this so that we don't accidentally send any further results 2463 mResponse = null; 2464 } 2465 cancelTimeout(); 2466 unbind(); 2467 } 2468 2469 @Override 2470 public void binderDied() { 2471 mResponse = null; 2472 close(); 2473 } 2474 2475 protected String toDebugString() { 2476 return toDebugString(SystemClock.elapsedRealtime()); 2477 } 2478 2479 protected String toDebugString(long now) { 2480 return "Session: expectLaunch " + mExpectActivityLaunch 2481 + ", connected " + (mAuthenticator != null) 2482 + ", stats (" + mNumResults + "/" + mNumRequestContinued 2483 + "/" + mNumErrors + ")" 2484 + ", lifetime " + ((now - mCreationTime) / 1000.0); 2485 } 2486 2487 void bind() { 2488 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2489 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 2490 } 2491 if (!bindToAuthenticator(mAccountType)) { 2492 Log.d(TAG, "bind attempt failed for " + toDebugString()); 2493 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 2494 } 2495 } 2496 2497 private void unbind() { 2498 if (mAuthenticator != null) { 2499 mAuthenticator = null; 2500 mContext.unbindService(this); 2501 } 2502 } 2503 2504 public void scheduleTimeout() { 2505 mMessageHandler.sendMessageDelayed( 2506 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS); 2507 } 2508 2509 public void cancelTimeout() { 2510 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 2511 } 2512 2513 @Override 2514 public void onServiceConnected(ComponentName name, IBinder service) { 2515 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 2516 try { 2517 run(); 2518 } catch (RemoteException e) { 2519 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2520 "remote exception"); 2521 } 2522 } 2523 2524 @Override 2525 public void onServiceDisconnected(ComponentName name) { 2526 mAuthenticator = null; 2527 IAccountManagerResponse response = getResponseAndClose(); 2528 if (response != null) { 2529 try { 2530 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2531 "disconnected"); 2532 } catch (RemoteException e) { 2533 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2534 Log.v(TAG, "Session.onServiceDisconnected: " 2535 + "caught RemoteException while responding", e); 2536 } 2537 } 2538 } 2539 } 2540 2541 public abstract void run() throws RemoteException; 2542 2543 public void onTimedOut() { 2544 IAccountManagerResponse response = getResponseAndClose(); 2545 if (response != null) { 2546 try { 2547 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2548 "timeout"); 2549 } catch (RemoteException e) { 2550 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2551 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 2552 e); 2553 } 2554 } 2555 } 2556 } 2557 2558 @Override 2559 public void onResult(Bundle result) { 2560 mNumResults++; 2561 Intent intent = null; 2562 if (result != null 2563 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 2564 /* 2565 * The Authenticator API allows third party authenticators to 2566 * supply arbitrary intents to other apps that they can run, 2567 * this can be very bad when those apps are in the system like 2568 * the System Settings. 2569 */ 2570 int authenticatorUid = Binder.getCallingUid(); 2571 long bid = Binder.clearCallingIdentity(); 2572 try { 2573 PackageManager pm = mContext.getPackageManager(); 2574 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 2575 int targetUid = resolveInfo.activityInfo.applicationInfo.uid; 2576 if (PackageManager.SIGNATURE_MATCH != 2577 pm.checkSignatures(authenticatorUid, targetUid)) { 2578 throw new SecurityException( 2579 "Activity to be started with KEY_INTENT must " + 2580 "share Authenticator's signatures"); 2581 } 2582 } finally { 2583 Binder.restoreCallingIdentity(bid); 2584 } 2585 } 2586 if (result != null 2587 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 2588 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 2589 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 2590 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 2591 Account account = new Account(accountName, accountType); 2592 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 2593 new UserHandle(mAccounts.userId)); 2594 } 2595 } 2596 IAccountManagerResponse response; 2597 if (mExpectActivityLaunch && result != null 2598 && result.containsKey(AccountManager.KEY_INTENT)) { 2599 response = mResponse; 2600 } else { 2601 response = getResponseAndClose(); 2602 } 2603 if (response != null) { 2604 try { 2605 if (result == null) { 2606 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2607 Log.v(TAG, getClass().getSimpleName() 2608 + " calling onError() on response " + response); 2609 } 2610 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 2611 "null bundle returned"); 2612 } else { 2613 if (mStripAuthTokenFromResult) { 2614 result.remove(AccountManager.KEY_AUTHTOKEN); 2615 } 2616 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2617 Log.v(TAG, getClass().getSimpleName() 2618 + " calling onResult() on response " + response); 2619 } 2620 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 2621 (intent == null)) { 2622 // All AccountManager error codes are greater than 0 2623 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 2624 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 2625 } else { 2626 response.onResult(result); 2627 } 2628 } 2629 } catch (RemoteException e) { 2630 // if the caller is dead then there is no one to care about remote exceptions 2631 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2632 Log.v(TAG, "failure while notifying response", e); 2633 } 2634 } 2635 } 2636 } 2637 2638 @Override 2639 public void onRequestContinued() { 2640 mNumRequestContinued++; 2641 } 2642 2643 @Override 2644 public void onError(int errorCode, String errorMessage) { 2645 mNumErrors++; 2646 IAccountManagerResponse response = getResponseAndClose(); 2647 if (response != null) { 2648 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2649 Log.v(TAG, getClass().getSimpleName() 2650 + " calling onError() on response " + response); 2651 } 2652 try { 2653 response.onError(errorCode, errorMessage); 2654 } catch (RemoteException e) { 2655 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2656 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 2657 } 2658 } 2659 } else { 2660 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2661 Log.v(TAG, "Session.onError: already closed"); 2662 } 2663 } 2664 } 2665 2666 /** 2667 * find the component name for the authenticator and initiate a bind 2668 * if no authenticator or the bind fails then return false, otherwise return true 2669 */ 2670 private boolean bindToAuthenticator(String authenticatorType) { 2671 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 2672 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 2673 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 2674 if (authenticatorInfo == null) { 2675 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2676 Log.v(TAG, "there is no authenticator for " + authenticatorType 2677 + ", bailing out"); 2678 } 2679 return false; 2680 } 2681 2682 Intent intent = new Intent(); 2683 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 2684 intent.setComponent(authenticatorInfo.componentName); 2685 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2686 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 2687 } 2688 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 2689 new UserHandle(mAccounts.userId))) { 2690 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2691 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 2692 } 2693 return false; 2694 } 2695 2696 2697 return true; 2698 } 2699 } 2700 2701 private class MessageHandler extends Handler { 2702 MessageHandler(Looper looper) { 2703 super(looper); 2704 } 2705 2706 @Override 2707 public void handleMessage(Message msg) { 2708 switch (msg.what) { 2709 case MESSAGE_TIMED_OUT: 2710 Session session = (Session)msg.obj; 2711 session.onTimedOut(); 2712 break; 2713 2714 case MESSAGE_COPY_SHARED_ACCOUNT: 2715 copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); 2716 break; 2717 2718 default: 2719 throw new IllegalStateException("unhandled message: " + msg.what); 2720 } 2721 } 2722 } 2723 2724 private static String getDatabaseName(int userId) { 2725 File systemDir = Environment.getSystemSecureDirectory(); 2726 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); 2727 if (userId == 0) { 2728 // Migrate old file, if it exists, to the new location. 2729 // Make sure the new file doesn't already exist. A dummy file could have been 2730 // accidentally created in the old location, causing the new one to become corrupted 2731 // as well. 2732 File oldFile = new File(systemDir, DATABASE_NAME); 2733 if (oldFile.exists() && !databaseFile.exists()) { 2734 // Check for use directory; create if it doesn't exist, else renameTo will fail 2735 File userDir = Environment.getUserSystemDirectory(userId); 2736 if (!userDir.exists()) { 2737 if (!userDir.mkdirs()) { 2738 throw new IllegalStateException("User dir cannot be created: " + userDir); 2739 } 2740 } 2741 if (!oldFile.renameTo(databaseFile)) { 2742 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 2743 } 2744 } 2745 } 2746 return databaseFile.getPath(); 2747 } 2748 2749 static class DatabaseHelper extends SQLiteOpenHelper { 2750 2751 public DatabaseHelper(Context context, int userId) { 2752 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); 2753 } 2754 2755 /** 2756 * This call needs to be made while the mCacheLock is held. The way to 2757 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 2758 * @param db The database. 2759 */ 2760 @Override 2761 public void onCreate(SQLiteDatabase db) { 2762 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 2763 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2764 + ACCOUNTS_NAME + " TEXT NOT NULL, " 2765 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 2766 + ACCOUNTS_PASSWORD + " TEXT, " 2767 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 2768 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 2769 2770 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 2771 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2772 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 2773 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 2774 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 2775 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 2776 2777 createGrantsTable(db); 2778 2779 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 2780 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2781 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 2782 + EXTRAS_KEY + " TEXT NOT NULL, " 2783 + EXTRAS_VALUE + " TEXT, " 2784 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 2785 2786 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 2787 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 2788 + META_VALUE + " TEXT)"); 2789 2790 createSharedAccountsTable(db); 2791 2792 createAccountsDeletionTrigger(db); 2793 } 2794 2795 private void createSharedAccountsTable(SQLiteDatabase db) { 2796 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 2797 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2798 + ACCOUNTS_NAME + " TEXT NOT NULL, " 2799 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 2800 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 2801 } 2802 2803 private void addOldAccountNameColumn(SQLiteDatabase db) { 2804 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 2805 } 2806 2807 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 2808 db.execSQL("" 2809 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 2810 + " BEGIN" 2811 + " DELETE FROM " + TABLE_AUTHTOKENS 2812 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2813 + " DELETE FROM " + TABLE_EXTRAS 2814 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2815 + " DELETE FROM " + TABLE_GRANTS 2816 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2817 + " END"); 2818 } 2819 2820 private void createGrantsTable(SQLiteDatabase db) { 2821 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 2822 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 2823 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 2824 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 2825 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 2826 + "," + GRANTS_GRANTEE_UID + "))"); 2827 } 2828 2829 @Override 2830 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 2831 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 2832 2833 if (oldVersion == 1) { 2834 // no longer need to do anything since the work is done 2835 // when upgrading from version 2 2836 oldVersion++; 2837 } 2838 2839 if (oldVersion == 2) { 2840 createGrantsTable(db); 2841 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 2842 createAccountsDeletionTrigger(db); 2843 oldVersion++; 2844 } 2845 2846 if (oldVersion == 3) { 2847 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 2848 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 2849 oldVersion++; 2850 } 2851 2852 if (oldVersion == 4) { 2853 createSharedAccountsTable(db); 2854 oldVersion++; 2855 } 2856 2857 if (oldVersion == 5) { 2858 addOldAccountNameColumn(db); 2859 oldVersion++; 2860 } 2861 2862 if (oldVersion != newVersion) { 2863 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 2864 } 2865 } 2866 2867 @Override 2868 public void onOpen(SQLiteDatabase db) { 2869 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 2870 } 2871 } 2872 2873 public IBinder onBind(Intent intent) { 2874 return asBinder(); 2875 } 2876 2877 /** 2878 * Searches array of arguments for the specified string 2879 * @param args array of argument strings 2880 * @param value value to search for 2881 * @return true if the value is contained in the array 2882 */ 2883 private static boolean scanArgs(String[] args, String value) { 2884 if (args != null) { 2885 for (String arg : args) { 2886 if (value.equals(arg)) { 2887 return true; 2888 } 2889 } 2890 } 2891 return false; 2892 } 2893 2894 @Override 2895 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 2896 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2897 != PackageManager.PERMISSION_GRANTED) { 2898 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 2899 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2900 + " without permission " + android.Manifest.permission.DUMP); 2901 return; 2902 } 2903 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 2904 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 2905 2906 final List<UserInfo> users = getUserManager().getUsers(); 2907 for (UserInfo user : users) { 2908 ipw.println("User " + user + ":"); 2909 ipw.increaseIndent(); 2910 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 2911 ipw.println(); 2912 ipw.decreaseIndent(); 2913 } 2914 } 2915 2916 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 2917 String[] args, boolean isCheckinRequest) { 2918 synchronized (userAccounts.cacheLock) { 2919 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 2920 2921 if (isCheckinRequest) { 2922 // This is a checkin request. *Only* upload the account types and the count of each. 2923 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 2924 null, null, ACCOUNTS_TYPE, null, null); 2925 try { 2926 while (cursor.moveToNext()) { 2927 // print type,count 2928 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 2929 } 2930 } finally { 2931 if (cursor != null) { 2932 cursor.close(); 2933 } 2934 } 2935 } else { 2936 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 2937 Process.myUid(), null); 2938 fout.println("Accounts: " + accounts.length); 2939 for (Account account : accounts) { 2940 fout.println(" " + account); 2941 } 2942 2943 fout.println(); 2944 synchronized (mSessions) { 2945 final long now = SystemClock.elapsedRealtime(); 2946 fout.println("Active Sessions: " + mSessions.size()); 2947 for (Session session : mSessions.values()) { 2948 fout.println(" " + session.toDebugString(now)); 2949 } 2950 } 2951 2952 fout.println(); 2953 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 2954 } 2955 } 2956 } 2957 2958 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 2959 Intent intent, int userId) { 2960 long identityToken = clearCallingIdentity(); 2961 try { 2962 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2963 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 2964 } 2965 2966 if (intent.getComponent() != null && 2967 GrantCredentialsPermissionActivity.class.getName().equals( 2968 intent.getComponent().getClassName())) { 2969 createNoCredentialsPermissionNotification(account, intent, userId); 2970 } else { 2971 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 2972 intent.addCategory(String.valueOf(notificationId)); 2973 Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 2974 0 /* when */); 2975 UserHandle user = new UserHandle(userId); 2976 Context contextForUser = getContextForUser(user); 2977 final String notificationTitleFormat = 2978 contextForUser.getText(R.string.notification_title).toString(); 2979 n.color = contextForUser.getResources().getColor( 2980 com.android.internal.R.color.system_notification_accent_color); 2981 n.setLatestEventInfo(contextForUser, 2982 String.format(notificationTitleFormat, account.name), 2983 message, PendingIntent.getActivityAsUser( 2984 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 2985 null, user)); 2986 installNotification(notificationId, n, user); 2987 } 2988 } finally { 2989 restoreCallingIdentity(identityToken); 2990 } 2991 } 2992 2993 protected void installNotification(final int notificationId, final Notification n, 2994 UserHandle user) { 2995 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 2996 .notifyAsUser(null, notificationId, n, user); 2997 } 2998 2999 protected void cancelNotification(int id, UserHandle user) { 3000 long identityToken = clearCallingIdentity(); 3001 try { 3002 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 3003 .cancelAsUser(null, id, user); 3004 } finally { 3005 restoreCallingIdentity(identityToken); 3006 } 3007 } 3008 3009 /** Succeeds if any of the specified permissions are granted. */ 3010 private void checkBinderPermission(String... permissions) { 3011 final int uid = Binder.getCallingUid(); 3012 3013 for (String perm : permissions) { 3014 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 3015 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3016 Log.v(TAG, " caller uid " + uid + " has " + perm); 3017 } 3018 return; 3019 } 3020 } 3021 3022 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions); 3023 Log.w(TAG, " " + msg); 3024 throw new SecurityException(msg); 3025 } 3026 3027 private int handleIncomingUser(int userId) { 3028 try { 3029 return ActivityManagerNative.getDefault().handleIncomingUser( 3030 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 3031 } catch (RemoteException re) { 3032 // Shouldn't happen, local. 3033 } 3034 return userId; 3035 } 3036 3037 private boolean isPrivileged(int callingUid) { 3038 final int callingUserId = UserHandle.getUserId(callingUid); 3039 3040 final PackageManager userPackageManager; 3041 try { 3042 userPackageManager = mContext.createPackageContextAsUser( 3043 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 3044 } catch (NameNotFoundException e) { 3045 return false; 3046 } 3047 3048 String[] packages = userPackageManager.getPackagesForUid(callingUid); 3049 for (String name : packages) { 3050 try { 3051 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 3052 if (packageInfo != null 3053 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { 3054 return true; 3055 } 3056 } catch (PackageManager.NameNotFoundException e) { 3057 return false; 3058 } 3059 } 3060 return false; 3061 } 3062 3063 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { 3064 final boolean isPrivileged = isPrivileged(callerUid); 3065 final boolean fromAuthenticator = account != null 3066 && hasAuthenticatorUid(account.type, callerUid); 3067 final boolean hasExplicitGrants = account != null 3068 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); 3069 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3070 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " 3071 + callerUid + ", " + account 3072 + ": is authenticator? " + fromAuthenticator 3073 + ", has explicit permission? " + hasExplicitGrants); 3074 } 3075 return fromAuthenticator || hasExplicitGrants || isPrivileged; 3076 } 3077 3078 private boolean hasAuthenticatorUid(String accountType, int callingUid) { 3079 final int callingUserId = UserHandle.getUserId(callingUid); 3080 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 3081 mAuthenticatorCache.getAllServices(callingUserId)) { 3082 if (serviceInfo.type.type.equals(accountType)) { 3083 return (serviceInfo.uid == callingUid) || 3084 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) 3085 == PackageManager.SIGNATURE_MATCH); 3086 } 3087 } 3088 return false; 3089 } 3090 3091 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 3092 int callerUid) { 3093 if (callerUid == Process.SYSTEM_UID) { 3094 return true; 3095 } 3096 UserAccounts accounts = getUserAccountsForCaller(); 3097 synchronized (accounts.cacheLock) { 3098 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3099 String[] args = { String.valueOf(callerUid), authTokenType, 3100 account.name, account.type}; 3101 final boolean permissionGranted = 3102 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; 3103 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 3104 // TODO: Skip this check when running automated tests. Replace this 3105 // with a more general solution. 3106 Log.d(TAG, "no credentials permission for usage of " + account + ", " 3107 + authTokenType + " by uid " + callerUid 3108 + " but ignoring since device is in test harness."); 3109 return true; 3110 } 3111 return permissionGranted; 3112 } 3113 } 3114 3115 private void checkCallingUidAgainstAuthenticator(Account account) { 3116 final int uid = Binder.getCallingUid(); 3117 if (account == null || !hasAuthenticatorUid(account.type, uid)) { 3118 String msg = "caller uid " + uid + " is different than the authenticator's uid"; 3119 Log.w(TAG, msg); 3120 throw new SecurityException(msg); 3121 } 3122 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3123 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid"); 3124 } 3125 } 3126 3127 private void checkAuthenticateAccountsPermission(Account account) { 3128 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS); 3129 checkCallingUidAgainstAuthenticator(account); 3130 } 3131 3132 private void checkReadAccountsPermission() { 3133 checkBinderPermission(Manifest.permission.GET_ACCOUNTS); 3134 } 3135 3136 private void checkManageAccountsPermission() { 3137 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS); 3138 } 3139 3140 private void checkManageAccountsOrUseCredentialsPermissions() { 3141 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS, 3142 Manifest.permission.USE_CREDENTIALS); 3143 } 3144 3145 private boolean canUserModifyAccounts(int userId) { 3146 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 3147 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 3148 return false; 3149 } 3150 return true; 3151 } 3152 3153 private boolean canUserModifyAccountsForType(int userId, String accountType) { 3154 DevicePolicyManager dpm = (DevicePolicyManager) mContext 3155 .getSystemService(Context.DEVICE_POLICY_SERVICE); 3156 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 3157 if (typesArray == null) { 3158 return true; 3159 } 3160 for (String forbiddenType : typesArray) { 3161 if (forbiddenType.equals(accountType)) { 3162 return false; 3163 } 3164 } 3165 return true; 3166 } 3167 3168 @Override 3169 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 3170 throws RemoteException { 3171 final int callingUid = getCallingUid(); 3172 3173 if (callingUid != Process.SYSTEM_UID) { 3174 throw new SecurityException(); 3175 } 3176 3177 if (value) { 3178 grantAppPermission(account, authTokenType, uid); 3179 } else { 3180 revokeAppPermission(account, authTokenType, uid); 3181 } 3182 } 3183 3184 /** 3185 * Allow callers with the given uid permission to get credentials for account/authTokenType. 3186 * <p> 3187 * Although this is public it can only be accessed via the AccountManagerService object 3188 * which is in the system. This means we don't need to protect it with permissions. 3189 * @hide 3190 */ 3191 private void grantAppPermission(Account account, String authTokenType, int uid) { 3192 if (account == null || authTokenType == null) { 3193 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 3194 return; 3195 } 3196 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3197 synchronized (accounts.cacheLock) { 3198 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3199 db.beginTransaction(); 3200 try { 3201 long accountId = getAccountIdLocked(db, account); 3202 if (accountId >= 0) { 3203 ContentValues values = new ContentValues(); 3204 values.put(GRANTS_ACCOUNTS_ID, accountId); 3205 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 3206 values.put(GRANTS_GRANTEE_UID, uid); 3207 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 3208 db.setTransactionSuccessful(); 3209 } 3210 } finally { 3211 db.endTransaction(); 3212 } 3213 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 3214 new UserHandle(accounts.userId)); 3215 } 3216 } 3217 3218 /** 3219 * Don't allow callers with the given uid permission to get credentials for 3220 * account/authTokenType. 3221 * <p> 3222 * Although this is public it can only be accessed via the AccountManagerService object 3223 * which is in the system. This means we don't need to protect it with permissions. 3224 * @hide 3225 */ 3226 private void revokeAppPermission(Account account, String authTokenType, int uid) { 3227 if (account == null || authTokenType == null) { 3228 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 3229 return; 3230 } 3231 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3232 synchronized (accounts.cacheLock) { 3233 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3234 db.beginTransaction(); 3235 try { 3236 long accountId = getAccountIdLocked(db, account); 3237 if (accountId >= 0) { 3238 db.delete(TABLE_GRANTS, 3239 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 3240 + GRANTS_GRANTEE_UID + "=?", 3241 new String[]{String.valueOf(accountId), authTokenType, 3242 String.valueOf(uid)}); 3243 db.setTransactionSuccessful(); 3244 } 3245 } finally { 3246 db.endTransaction(); 3247 } 3248 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 3249 new UserHandle(accounts.userId)); 3250 } 3251 } 3252 3253 static final private String stringArrayToString(String[] value) { 3254 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 3255 } 3256 3257 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 3258 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 3259 if (oldAccountsForType != null) { 3260 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 3261 for (Account curAccount : oldAccountsForType) { 3262 if (!curAccount.equals(account)) { 3263 newAccountsList.add(curAccount); 3264 } 3265 } 3266 if (newAccountsList.isEmpty()) { 3267 accounts.accountCache.remove(account.type); 3268 } else { 3269 Account[] newAccountsForType = new Account[newAccountsList.size()]; 3270 newAccountsForType = newAccountsList.toArray(newAccountsForType); 3271 accounts.accountCache.put(account.type, newAccountsForType); 3272 } 3273 } 3274 accounts.userDataCache.remove(account); 3275 accounts.authTokenCache.remove(account); 3276 accounts.previousNameCache.remove(account); 3277 } 3278 3279 /** 3280 * This assumes that the caller has already checked that the account is not already present. 3281 */ 3282 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 3283 Account[] accountsForType = accounts.accountCache.get(account.type); 3284 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 3285 Account[] newAccountsForType = new Account[oldLength + 1]; 3286 if (accountsForType != null) { 3287 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 3288 } 3289 newAccountsForType[oldLength] = account; 3290 accounts.accountCache.put(account.type, newAccountsForType); 3291 } 3292 3293 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 3294 int callingUid, String callingPackage) { 3295 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 3296 || callingUid == Process.myUid()) { 3297 return unfiltered; 3298 } 3299 UserInfo user = mUserManager.getUserInfo(userAccounts.userId); 3300 if (user != null && user.isRestricted()) { 3301 String[] packages = mPackageManager.getPackagesForUid(callingUid); 3302 // If any of the packages is a white listed package, return the full set, 3303 // otherwise return non-shared accounts only. 3304 // This might be a temporary way to specify a whitelist 3305 String whiteList = mContext.getResources().getString( 3306 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 3307 for (String packageName : packages) { 3308 if (whiteList.contains(";" + packageName + ";")) { 3309 return unfiltered; 3310 } 3311 } 3312 ArrayList<Account> allowed = new ArrayList<Account>(); 3313 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 3314 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 3315 String requiredAccountType = ""; 3316 try { 3317 // If there's an explicit callingPackage specified, check if that package 3318 // opted in to see restricted accounts. 3319 if (callingPackage != null) { 3320 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 3321 if (pi != null && pi.restrictedAccountType != null) { 3322 requiredAccountType = pi.restrictedAccountType; 3323 } 3324 } else { 3325 // Otherwise check if the callingUid has a package that has opted in 3326 for (String packageName : packages) { 3327 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 3328 if (pi != null && pi.restrictedAccountType != null) { 3329 requiredAccountType = pi.restrictedAccountType; 3330 break; 3331 } 3332 } 3333 } 3334 } catch (NameNotFoundException nnfe) { 3335 } 3336 for (Account account : unfiltered) { 3337 if (account.type.equals(requiredAccountType)) { 3338 allowed.add(account); 3339 } else { 3340 boolean found = false; 3341 for (Account shared : sharedAccounts) { 3342 if (shared.equals(account)) { 3343 found = true; 3344 break; 3345 } 3346 } 3347 if (!found) { 3348 allowed.add(account); 3349 } 3350 } 3351 } 3352 Account[] filtered = new Account[allowed.size()]; 3353 allowed.toArray(filtered); 3354 return filtered; 3355 } else { 3356 return unfiltered; 3357 } 3358 } 3359 3360 /* 3361 * packageName can be null. If not null, it should be used to filter out restricted accounts 3362 * that the package is not allowed to access. 3363 */ 3364 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 3365 int callingUid, String callingPackage) { 3366 if (accountType != null) { 3367 final Account[] accounts = userAccounts.accountCache.get(accountType); 3368 if (accounts == null) { 3369 return EMPTY_ACCOUNT_ARRAY; 3370 } else { 3371 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 3372 callingUid, callingPackage); 3373 } 3374 } else { 3375 int totalLength = 0; 3376 for (Account[] accounts : userAccounts.accountCache.values()) { 3377 totalLength += accounts.length; 3378 } 3379 if (totalLength == 0) { 3380 return EMPTY_ACCOUNT_ARRAY; 3381 } 3382 Account[] accounts = new Account[totalLength]; 3383 totalLength = 0; 3384 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 3385 System.arraycopy(accountsOfType, 0, accounts, totalLength, 3386 accountsOfType.length); 3387 totalLength += accountsOfType.length; 3388 } 3389 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 3390 } 3391 } 3392 3393 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 3394 Account account, String key, String value) { 3395 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 3396 if (userDataForAccount == null) { 3397 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 3398 accounts.userDataCache.put(account, userDataForAccount); 3399 } 3400 if (value == null) { 3401 userDataForAccount.remove(key); 3402 } else { 3403 userDataForAccount.put(key, value); 3404 } 3405 } 3406 3407 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 3408 Account account, String key, String value) { 3409 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 3410 if (authTokensForAccount == null) { 3411 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 3412 accounts.authTokenCache.put(account, authTokensForAccount); 3413 } 3414 if (value == null) { 3415 authTokensForAccount.remove(key); 3416 } else { 3417 authTokensForAccount.put(key, value); 3418 } 3419 } 3420 3421 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 3422 String authTokenType) { 3423 synchronized (accounts.cacheLock) { 3424 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 3425 if (authTokensForAccount == null) { 3426 // need to populate the cache for this account 3427 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3428 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 3429 accounts.authTokenCache.put(account, authTokensForAccount); 3430 } 3431 return authTokensForAccount.get(authTokenType); 3432 } 3433 } 3434 3435 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { 3436 synchronized (accounts.cacheLock) { 3437 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 3438 if (userDataForAccount == null) { 3439 // need to populate the cache for this account 3440 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3441 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 3442 accounts.userDataCache.put(account, userDataForAccount); 3443 } 3444 return userDataForAccount.get(key); 3445 } 3446 } 3447 3448 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 3449 final SQLiteDatabase db, Account account) { 3450 HashMap<String, String> userDataForAccount = new HashMap<String, String>(); 3451 Cursor cursor = db.query(TABLE_EXTRAS, 3452 COLUMNS_EXTRAS_KEY_AND_VALUE, 3453 SELECTION_USERDATA_BY_ACCOUNT, 3454 new String[]{account.name, account.type}, 3455 null, null, null); 3456 try { 3457 while (cursor.moveToNext()) { 3458 final String tmpkey = cursor.getString(0); 3459 final String value = cursor.getString(1); 3460 userDataForAccount.put(tmpkey, value); 3461 } 3462 } finally { 3463 cursor.close(); 3464 } 3465 return userDataForAccount; 3466 } 3467 3468 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 3469 final SQLiteDatabase db, Account account) { 3470 HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); 3471 Cursor cursor = db.query(TABLE_AUTHTOKENS, 3472 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 3473 SELECTION_AUTHTOKENS_BY_ACCOUNT, 3474 new String[]{account.name, account.type}, 3475 null, null, null); 3476 try { 3477 while (cursor.moveToNext()) { 3478 final String type = cursor.getString(0); 3479 final String authToken = cursor.getString(1); 3480 authTokensForAccount.put(type, authToken); 3481 } 3482 } finally { 3483 cursor.close(); 3484 } 3485 return authTokensForAccount; 3486 } 3487 3488 private Context getContextForUser(UserHandle user) { 3489 try { 3490 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 3491 } catch (NameNotFoundException e) { 3492 // Default to mContext, not finding the package system is running as is unlikely. 3493 return mContext; 3494 } 3495 } 3496} 3497