AccountManager.java revision 734f8fb69d0c8af7b10bded718897a55ced49bf0
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 android.accounts; 18 19import android.annotation.RequiresPermission; 20import android.annotation.Size; 21import android.app.Activity; 22import android.content.BroadcastReceiver; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.res.Resources; 28import android.database.SQLException; 29import android.os.Build; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.Looper; 33import android.os.Parcelable; 34import android.os.Process; 35import android.os.RemoteException; 36import android.os.UserHandle; 37import android.text.TextUtils; 38import android.util.Log; 39 40import com.android.internal.R; 41import com.google.android.collect.Maps; 42 43import java.io.IOException; 44import java.util.ArrayList; 45import java.util.HashMap; 46import java.util.Map; 47import java.util.concurrent.Callable; 48import java.util.concurrent.CancellationException; 49import java.util.concurrent.ExecutionException; 50import java.util.concurrent.FutureTask; 51import java.util.concurrent.TimeUnit; 52import java.util.concurrent.TimeoutException; 53 54import static android.Manifest.permission.AUTHENTICATE_ACCOUNTS; 55import static android.Manifest.permission.GET_ACCOUNTS; 56import static android.Manifest.permission.MANAGE_ACCOUNTS; 57import static android.Manifest.permission.USE_CREDENTIALS; 58 59/** 60 * This class provides access to a centralized registry of the user's 61 * online accounts. The user enters credentials (username and password) once 62 * per account, granting applications access to online resources with 63 * "one-click" approval. 64 * 65 * <p>Different online services have different ways of handling accounts and 66 * authentication, so the account manager uses pluggable <em>authenticator</em> 67 * modules for different <em>account types</em>. Authenticators (which may be 68 * written by third parties) handle the actual details of validating account 69 * credentials and storing account information. For example, Google, Facebook, 70 * and Microsoft Exchange each have their own authenticator. 71 * 72 * <p>Many servers support some notion of an <em>authentication token</em>, 73 * which can be used to authenticate a request to the server without sending 74 * the user's actual password. (Auth tokens are normally created with a 75 * separate request which does include the user's credentials.) AccountManager 76 * can generate auth tokens for applications, so the application doesn't need to 77 * handle passwords directly. Auth tokens are normally reusable and cached by 78 * AccountManager, but must be refreshed periodically. It's the responsibility 79 * of applications to <em>invalidate</em> auth tokens when they stop working so 80 * the AccountManager knows it needs to regenerate them. 81 * 82 * <p>Applications accessing a server normally go through these steps: 83 * 84 * <ul> 85 * <li>Get an instance of AccountManager using {@link #get(Context)}. 86 * 87 * <li>List the available accounts using {@link #getAccountsByType} or 88 * {@link #getAccountsByTypeAndFeatures}. Normally applications will only 89 * be interested in accounts with one particular <em>type</em>, which 90 * identifies the authenticator. Account <em>features</em> are used to 91 * identify particular account subtypes and capabilities. Both the account 92 * type and features are authenticator-specific strings, and must be known by 93 * the application in coordination with its preferred authenticators. 94 * 95 * <li>Select one or more of the available accounts, possibly by asking the 96 * user for their preference. If no suitable accounts are available, 97 * {@link #addAccount} may be called to prompt the user to create an 98 * account of the appropriate type. 99 * 100 * <li><b>Important:</b> If the application is using a previously remembered 101 * account selection, it must make sure the account is still in the list 102 * of accounts returned by {@link #getAccountsByType}. Requesting an auth token 103 * for an account no longer on the device results in an undefined failure. 104 * 105 * <li>Request an auth token for the selected account(s) using one of the 106 * {@link #getAuthToken} methods or related helpers. Refer to the description 107 * of each method for exact usage and error handling details. 108 * 109 * <li>Make the request using the auth token. The form of the auth token, 110 * the format of the request, and the protocol used are all specific to the 111 * service you are accessing. The application may use whatever network and 112 * protocol libraries are useful. 113 * 114 * <li><b>Important:</b> If the request fails with an authentication error, 115 * it could be that a cached auth token is stale and no longer honored by 116 * the server. The application must call {@link #invalidateAuthToken} to remove 117 * the token from the cache, otherwise requests will continue failing! After 118 * invalidating the auth token, immediately go back to the "Request an auth 119 * token" step above. If the process fails the second time, then it can be 120 * treated as a "genuine" authentication failure and the user notified or other 121 * appropriate actions taken. 122 * </ul> 123 * 124 * <p>Some AccountManager methods may need to interact with the user to 125 * prompt for credentials, present options, or ask the user to add an account. 126 * The caller may choose whether to allow AccountManager to directly launch the 127 * necessary user interface and wait for the user, or to return an Intent which 128 * the caller may use to launch the interface, or (in some cases) to install a 129 * notification which the user can select at any time to launch the interface. 130 * To have AccountManager launch the interface directly, the caller must supply 131 * the current foreground {@link Activity} context. 132 * 133 * <p>Many AccountManager methods take {@link AccountManagerCallback} and 134 * {@link Handler} as parameters. These methods return immediately and 135 * run asynchronously. If a callback is provided then 136 * {@link AccountManagerCallback#run} will be invoked on the Handler's 137 * thread when the request completes, successfully or not. 138 * The result is retrieved by calling {@link AccountManagerFuture#getResult()} 139 * on the {@link AccountManagerFuture} returned by the method (and also passed 140 * to the callback). This method waits for the operation to complete (if 141 * necessary) and either returns the result or throws an exception if an error 142 * occurred during the operation. To make the request synchronously, call 143 * {@link AccountManagerFuture#getResult()} immediately on receiving the 144 * future from the method; no callback need be supplied. 145 * 146 * <p>Requests which may block, including 147 * {@link AccountManagerFuture#getResult()}, must never be called on 148 * the application's main event thread. These operations throw 149 * {@link IllegalStateException} if they are used on the main thread. 150 */ 151public class AccountManager { 152 private static final String TAG = "AccountManager"; 153 154 public static final int ERROR_CODE_REMOTE_EXCEPTION = 1; 155 public static final int ERROR_CODE_NETWORK_ERROR = 3; 156 public static final int ERROR_CODE_CANCELED = 4; 157 public static final int ERROR_CODE_INVALID_RESPONSE = 5; 158 public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6; 159 public static final int ERROR_CODE_BAD_ARGUMENTS = 7; 160 public static final int ERROR_CODE_BAD_REQUEST = 8; 161 public static final int ERROR_CODE_BAD_AUTHENTICATION = 9; 162 163 /** @hide */ 164 public static final int ERROR_CODE_USER_RESTRICTED = 100; 165 /** @hide */ 166 public static final int ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101; 167 168 /** 169 * Bundle key used for the {@link String} account name in results 170 * from methods which return information about a particular account. 171 */ 172 public static final String KEY_ACCOUNT_NAME = "authAccount"; 173 174 /** 175 * Bundle key used for the {@link String} account type in results 176 * from methods which return information about a particular account. 177 */ 178 public static final String KEY_ACCOUNT_TYPE = "accountType"; 179 180 /** 181 * Bundle key used for the auth token value in results 182 * from {@link #getAuthToken} and friends. 183 */ 184 public static final String KEY_AUTHTOKEN = "authtoken"; 185 186 /** 187 * Bundle key used for an {@link Intent} in results from methods that 188 * may require the caller to interact with the user. The Intent can 189 * be used to start the corresponding user interface activity. 190 */ 191 public static final String KEY_INTENT = "intent"; 192 193 /** 194 * Bundle key used to supply the password directly in options to 195 * {@link #confirmCredentials}, rather than prompting the user with 196 * the standard password prompt. 197 */ 198 public static final String KEY_PASSWORD = "password"; 199 200 public static final String KEY_ACCOUNTS = "accounts"; 201 202 public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse"; 203 public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse"; 204 public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types"; 205 public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage"; 206 public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey"; 207 public static final String KEY_BOOLEAN_RESULT = "booleanResult"; 208 public static final String KEY_ERROR_CODE = "errorCode"; 209 public static final String KEY_ERROR_MESSAGE = "errorMessage"; 210 public static final String KEY_USERDATA = "userdata"; 211 212 /** 213 * Bundle key used to supply the last time the credentials of the account 214 * were authenticated successfully. Time is specified in milliseconds since 215 * epoch. 216 */ 217 public static final String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime"; 218 219 /** 220 * Authenticators using 'customTokens' option will also get the UID of the 221 * caller 222 */ 223 public static final String KEY_CALLER_UID = "callerUid"; 224 public static final String KEY_CALLER_PID = "callerPid"; 225 226 /** 227 * The Android package of the caller will be set in the options bundle by the 228 * {@link AccountManager} and will be passed to the AccountManagerService and 229 * to the AccountAuthenticators. The uid of the caller will be known by the 230 * AccountManagerService as well as the AccountAuthenticators so they will be able to 231 * verify that the package is consistent with the uid (a uid might be shared by many 232 * packages). 233 */ 234 public static final String KEY_ANDROID_PACKAGE_NAME = "androidPackageName"; 235 236 /** 237 * Boolean, if set and 'customTokens' the authenticator is responsible for 238 * notifications. 239 * @hide 240 */ 241 public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure"; 242 243 public static final String ACTION_AUTHENTICATOR_INTENT = 244 "android.accounts.AccountAuthenticator"; 245 public static final String AUTHENTICATOR_META_DATA_NAME = 246 "android.accounts.AccountAuthenticator"; 247 public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; 248 249 private final Context mContext; 250 private final IAccountManager mService; 251 private final Handler mMainHandler; 252 253 /** 254 * Action sent as a broadcast Intent by the AccountsService 255 * when accounts are added, accounts are removed, or an 256 * account's credentials (saved password, etc) are changed. 257 * 258 * @see #addOnAccountsUpdatedListener 259 */ 260 public static final String LOGIN_ACCOUNTS_CHANGED_ACTION = 261 "android.accounts.LOGIN_ACCOUNTS_CHANGED"; 262 263 /** 264 * @hide 265 */ 266 public AccountManager(Context context, IAccountManager service) { 267 mContext = context; 268 mService = service; 269 mMainHandler = new Handler(mContext.getMainLooper()); 270 } 271 272 /** 273 * @hide used for testing only 274 */ 275 public AccountManager(Context context, IAccountManager service, Handler handler) { 276 mContext = context; 277 mService = service; 278 mMainHandler = handler; 279 } 280 281 /** 282 * @hide for internal use only 283 */ 284 public static Bundle sanitizeResult(Bundle result) { 285 if (result != null) { 286 if (result.containsKey(KEY_AUTHTOKEN) 287 && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) { 288 final Bundle newResult = new Bundle(result); 289 newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>"); 290 return newResult; 291 } 292 } 293 return result; 294 } 295 296 /** 297 * Gets an AccountManager instance associated with a Context. 298 * The {@link Context} will be used as long as the AccountManager is 299 * active, so make sure to use a {@link Context} whose lifetime is 300 * commensurate with any listeners registered to 301 * {@link #addOnAccountsUpdatedListener} or similar methods. 302 * 303 * <p>It is safe to call this method from the main thread. 304 * 305 * <p>No permission is required to call this method. 306 * 307 * @param context The {@link Context} to use when necessary 308 * @return An {@link AccountManager} instance 309 */ 310 public static AccountManager get(Context context) { 311 if (context == null) throw new IllegalArgumentException("context is null"); 312 return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 313 } 314 315 /** 316 * Gets the saved password associated with the account. 317 * This is intended for authenticators and related code; applications 318 * should get an auth token instead. 319 * 320 * <p>It is safe to call this method from the main thread. 321 * 322 * <p>This method requires the caller to hold the permission 323 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 324 * and to have the same UID as the account's authenticator. 325 * 326 * @param account The account to query for a password 327 * @return The account's password, null if none or if the account doesn't exist 328 */ 329 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 330 public String getPassword(final Account account) { 331 if (account == null) throw new IllegalArgumentException("account is null"); 332 try { 333 return mService.getPassword(account); 334 } catch (RemoteException e) { 335 // will never happen 336 throw new RuntimeException(e); 337 } 338 } 339 340 /** 341 * Gets the user data named by "key" associated with the account. 342 * This is intended for authenticators and related code to store 343 * arbitrary metadata along with accounts. The meaning of the keys 344 * and values is up to the authenticator for the account. 345 * 346 * <p>It is safe to call this method from the main thread. 347 * 348 * <p>This method requires the caller to hold the permission 349 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 350 * and to have the same UID as the account's authenticator. 351 * 352 * @param account The account to query for user data 353 * @return The user data, null if the account or key doesn't exist 354 */ 355 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 356 public String getUserData(final Account account, final String key) { 357 if (account == null) throw new IllegalArgumentException("account is null"); 358 if (key == null) throw new IllegalArgumentException("key is null"); 359 try { 360 return mService.getUserData(account, key); 361 } catch (RemoteException e) { 362 // will never happen 363 throw new RuntimeException(e); 364 } 365 } 366 367 /** 368 * Lists the currently registered authenticators. 369 * 370 * <p>It is safe to call this method from the main thread. 371 * 372 * <p>No permission is required to call this method. 373 * 374 * @return An array of {@link AuthenticatorDescription} for every 375 * authenticator known to the AccountManager service. Empty (never 376 * null) if no authenticators are known. 377 */ 378 public AuthenticatorDescription[] getAuthenticatorTypes() { 379 try { 380 return mService.getAuthenticatorTypes(UserHandle.getCallingUserId()); 381 } catch (RemoteException e) { 382 // will never happen 383 throw new RuntimeException(e); 384 } 385 } 386 387 /** 388 * @hide 389 * Lists the currently registered authenticators for a given user id. 390 * 391 * <p>It is safe to call this method from the main thread. 392 * 393 * <p>The caller has to be in the same user or have the permission 394 * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. 395 * 396 * @return An array of {@link AuthenticatorDescription} for every 397 * authenticator known to the AccountManager service. Empty (never 398 * null) if no authenticators are known. 399 */ 400 public AuthenticatorDescription[] getAuthenticatorTypesAsUser(int userId) { 401 try { 402 return mService.getAuthenticatorTypes(userId); 403 } catch (RemoteException e) { 404 // will never happen 405 throw new RuntimeException(e); 406 } 407 } 408 409 /** 410 * Lists all accounts of any type registered on the device. 411 * Equivalent to getAccountsByType(null). 412 * 413 * <p>It is safe to call this method from the main thread. 414 * 415 * <p>This method requires the caller to hold the permission 416 * {@link android.Manifest.permission#GET_ACCOUNTS}. 417 * 418 * @return An array of {@link Account}, one for each account. Empty 419 * (never null) if no accounts have been added. 420 */ 421 @RequiresPermission(GET_ACCOUNTS) 422 public Account[] getAccounts() { 423 try { 424 return mService.getAccounts(null); 425 } catch (RemoteException e) { 426 // won't ever happen 427 throw new RuntimeException(e); 428 } 429 } 430 431 /** 432 * @hide 433 * Lists all accounts of any type registered on the device for a given 434 * user id. Equivalent to getAccountsByType(null). 435 * 436 * <p>It is safe to call this method from the main thread. 437 * 438 * <p>This method requires the caller to hold the permission 439 * {@link android.Manifest.permission#GET_ACCOUNTS}. 440 * 441 * @return An array of {@link Account}, one for each account. Empty 442 * (never null) if no accounts have been added. 443 */ 444 @RequiresPermission(GET_ACCOUNTS) 445 public Account[] getAccountsAsUser(int userId) { 446 try { 447 return mService.getAccountsAsUser(null, userId); 448 } catch (RemoteException e) { 449 // won't ever happen 450 throw new RuntimeException(e); 451 } 452 } 453 454 /** 455 * @hide 456 * For use by internal activities. Returns the list of accounts that the calling package 457 * is authorized to use, particularly for shared accounts. 458 * @param packageName package name of the calling app. 459 * @param uid the uid of the calling app. 460 * @return the accounts that are available to this package and user. 461 */ 462 public Account[] getAccountsForPackage(String packageName, int uid) { 463 try { 464 return mService.getAccountsForPackage(packageName, uid); 465 } catch (RemoteException re) { 466 // possible security exception 467 throw new RuntimeException(re); 468 } 469 } 470 471 /** 472 * Returns the accounts visible to the specified package, in an environment where some apps 473 * are not authorized to view all accounts. This method can only be called by system apps. 474 * @param type The type of accounts to return, null to retrieve all accounts 475 * @param packageName The package name of the app for which the accounts are to be returned 476 * @return An array of {@link Account}, one per matching account. Empty 477 * (never null) if no accounts of the specified type have been added. 478 */ 479 public Account[] getAccountsByTypeForPackage(String type, String packageName) { 480 try { 481 return mService.getAccountsByTypeForPackage(type, packageName); 482 } catch (RemoteException re) { 483 // possible security exception 484 throw new RuntimeException(re); 485 } 486 } 487 488 /** 489 * Lists all accounts of a particular type. The account type is a 490 * string token corresponding to the authenticator and useful domain 491 * of the account. For example, there are types corresponding to Google 492 * and Facebook. The exact string token to use will be published somewhere 493 * associated with the authenticator in question. 494 * 495 * <p>It is safe to call this method from the main thread. 496 * 497 * <p>This method requires the caller to hold the permission 498 * {@link android.Manifest.permission#GET_ACCOUNTS}. 499 * 500 * @param type The type of accounts to return, null to retrieve all accounts 501 * @return An array of {@link Account}, one per matching account. Empty 502 * (never null) if no accounts of the specified type have been added. 503 */ 504 @RequiresPermission(GET_ACCOUNTS) 505 public Account[] getAccountsByType(String type) { 506 return getAccountsByTypeAsUser(type, Process.myUserHandle()); 507 } 508 509 /** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */ 510 public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) { 511 try { 512 return mService.getAccountsAsUser(type, userHandle.getIdentifier()); 513 } catch (RemoteException e) { 514 // won't ever happen 515 throw new RuntimeException(e); 516 } 517 } 518 519 /** 520 * Change whether or not an app (identified by its uid) is allowed to retrieve an authToken 521 * for an account. 522 * <p> 523 * This is only meant to be used by system activities and is not in the SDK. 524 * @param account The account whose permissions are being modified 525 * @param authTokenType The type of token whose permissions are being modified 526 * @param uid The uid that identifies the app which is being granted or revoked permission. 527 * @param value true is permission is being granted, false for revoked 528 * @hide 529 */ 530 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) { 531 try { 532 mService.updateAppPermission(account, authTokenType, uid, value); 533 } catch (RemoteException e) { 534 // won't ever happen 535 throw new RuntimeException(e); 536 } 537 } 538 539 /** 540 * Get the user-friendly label associated with an authenticator's auth token. 541 * @param accountType the type of the authenticator. must not be null. 542 * @param authTokenType the token type. must not be null. 543 * @param callback callback to invoke when the result is available. may be null. 544 * @param handler the handler on which to invoke the callback, or null for the main thread 545 * @return a future containing the label string 546 * @hide 547 */ 548 public AccountManagerFuture<String> getAuthTokenLabel( 549 final String accountType, final String authTokenType, 550 AccountManagerCallback<String> callback, Handler handler) { 551 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 552 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 553 return new Future2Task<String>(handler, callback) { 554 public void doWork() throws RemoteException { 555 mService.getAuthTokenLabel(mResponse, accountType, authTokenType); 556 } 557 558 @Override 559 public String bundleToResult(Bundle bundle) throws AuthenticatorException { 560 if (!bundle.containsKey(KEY_AUTH_TOKEN_LABEL)) { 561 throw new AuthenticatorException("no result in response"); 562 } 563 return bundle.getString(KEY_AUTH_TOKEN_LABEL); 564 } 565 }.start(); 566 } 567 568 /** 569 * Finds out whether a particular account has all the specified features. 570 * Account features are authenticator-specific string tokens identifying 571 * boolean account properties. For example, features are used to tell 572 * whether Google accounts have a particular service (such as Google 573 * Calendar or Google Talk) enabled. The feature names and their meanings 574 * are published somewhere associated with the authenticator in question. 575 * 576 * <p>This method may be called from any thread, but the returned 577 * {@link AccountManagerFuture} must not be used on the main thread. 578 * 579 * <p>This method requires the caller to hold the permission 580 * {@link android.Manifest.permission#GET_ACCOUNTS}. 581 * 582 * @param account The {@link Account} to test 583 * @param features An array of the account features to check 584 * @param callback Callback to invoke when the request completes, 585 * null for no callback 586 * @param handler {@link Handler} identifying the callback thread, 587 * null for the main thread 588 * @return An {@link AccountManagerFuture} which resolves to a Boolean, 589 * true if the account exists and has all of the specified features. 590 */ 591 @RequiresPermission(GET_ACCOUNTS) 592 public AccountManagerFuture<Boolean> hasFeatures(final Account account, 593 final String[] features, 594 AccountManagerCallback<Boolean> callback, Handler handler) { 595 if (account == null) throw new IllegalArgumentException("account is null"); 596 if (features == null) throw new IllegalArgumentException("features is null"); 597 return new Future2Task<Boolean>(handler, callback) { 598 public void doWork() throws RemoteException { 599 mService.hasFeatures(mResponse, account, features); 600 } 601 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { 602 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { 603 throw new AuthenticatorException("no result in response"); 604 } 605 return bundle.getBoolean(KEY_BOOLEAN_RESULT); 606 } 607 }.start(); 608 } 609 610 /** 611 * Lists all accounts of a type which have certain features. The account 612 * type identifies the authenticator (see {@link #getAccountsByType}). 613 * Account features are authenticator-specific string tokens identifying 614 * boolean account properties (see {@link #hasFeatures}). 615 * 616 * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator, 617 * which may contact the server or do other work to check account features, 618 * so the method returns an {@link AccountManagerFuture}. 619 * 620 * <p>This method may be called from any thread, but the returned 621 * {@link AccountManagerFuture} must not be used on the main thread. 622 * 623 * <p>This method requires the caller to hold the permission 624 * {@link android.Manifest.permission#GET_ACCOUNTS}. 625 * 626 * @param type The type of accounts to return, must not be null 627 * @param features An array of the account features to require, 628 * may be null or empty 629 * @param callback Callback to invoke when the request completes, 630 * null for no callback 631 * @param handler {@link Handler} identifying the callback thread, 632 * null for the main thread 633 * @return An {@link AccountManagerFuture} which resolves to an array of 634 * {@link Account}, one per account of the specified type which 635 * matches the requested features. 636 */ 637 @RequiresPermission(GET_ACCOUNTS) 638 public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( 639 final String type, final String[] features, 640 AccountManagerCallback<Account[]> callback, Handler handler) { 641 if (type == null) throw new IllegalArgumentException("type is null"); 642 return new Future2Task<Account[]>(handler, callback) { 643 public void doWork() throws RemoteException { 644 mService.getAccountsByFeatures(mResponse, type, features); 645 } 646 public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { 647 if (!bundle.containsKey(KEY_ACCOUNTS)) { 648 throw new AuthenticatorException("no result in response"); 649 } 650 final Parcelable[] parcelables = bundle.getParcelableArray(KEY_ACCOUNTS); 651 Account[] descs = new Account[parcelables.length]; 652 for (int i = 0; i < parcelables.length; i++) { 653 descs[i] = (Account) parcelables[i]; 654 } 655 return descs; 656 } 657 }.start(); 658 } 659 660 /** 661 * Adds an account directly to the AccountManager. Normally used by sign-up 662 * wizards associated with authenticators, not directly by applications. 663 * 664 * <p>It is safe to call this method from the main thread. 665 * 666 * <p>This method requires the caller to hold the permission 667 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 668 * and to have the same UID as the added account's authenticator. 669 * 670 * @param account The {@link Account} to add 671 * @param password The password to associate with the account, null for none 672 * @param userdata String values to use for the account's userdata, null for none 673 * @return True if the account was successfully added, false if the account 674 * already exists, the account is null, or another error occurs. 675 */ 676 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 677 public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { 678 if (account == null) throw new IllegalArgumentException("account is null"); 679 try { 680 return mService.addAccountExplicitly(account, password, userdata); 681 } catch (RemoteException e) { 682 // won't ever happen 683 throw new RuntimeException(e); 684 } 685 } 686 687 /** 688 * Notifies the system that the account has just been authenticated. This 689 * information may be used by other applications to verify the account. This 690 * should be called only when the user has entered correct credentials for 691 * the account. 692 * <p> 693 * It is not safe to call this method from the main thread. As such, call it 694 * from another thread. 695 * <p> 696 * This method requires the caller to hold the permission 697 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be 698 * called from the account's authenticator. 699 * 700 * @param account The {@link Account} to be updated. 701 */ 702 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 703 public boolean notifyAccountAuthenticated(Account account) { 704 if (account == null) 705 throw new IllegalArgumentException("account is null"); 706 try { 707 return mService.accountAuthenticated(account); 708 } catch (RemoteException e) { 709 throw new RuntimeException(e); 710 } 711 } 712 713 /** 714 * Rename the specified {@link Account}. This is equivalent to removing 715 * the existing account and adding a new renamed account with the old 716 * account's user data. 717 * 718 * <p>It is safe to call this method from the main thread. 719 * 720 * <p>This method requires the caller to hold the permission 721 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 722 * and have the same UID as the account's authenticator. 723 * 724 * @param account The {@link Account} to rename 725 * @param newName String name to be associated with the account. 726 * @param callback Callback to invoke when the request completes, null for 727 * no callback 728 * @param handler {@link Handler} identifying the callback thread, null for 729 * the main thread 730 * @return An {@link AccountManagerFuture} which resolves to the Account 731 * after the name change. If successful the account's name will be the 732 * specified new name. 733 */ 734 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 735 public AccountManagerFuture<Account> renameAccount( 736 final Account account, 737 @Size(min = 1) final String newName, 738 AccountManagerCallback<Account> callback, 739 Handler handler) { 740 if (account == null) throw new IllegalArgumentException("account is null."); 741 if (TextUtils.isEmpty(newName)) { 742 throw new IllegalArgumentException("newName is empty or null."); 743 } 744 return new Future2Task<Account>(handler, callback) { 745 @Override 746 public void doWork() throws RemoteException { 747 mService.renameAccount(mResponse, account, newName); 748 } 749 @Override 750 public Account bundleToResult(Bundle bundle) throws AuthenticatorException { 751 String name = bundle.getString(KEY_ACCOUNT_NAME); 752 String type = bundle.getString(KEY_ACCOUNT_TYPE); 753 return new Account(name, type); 754 } 755 }.start(); 756 } 757 758 /** 759 * Gets the previous name associated with the account or {@code null}, if 760 * none. This is intended so that clients of {@link 761 * #LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts can determine if an 762 * authenticator has renamed an account. 763 * 764 * <p>It is safe to call this method from the main thread. 765 * 766 * @param account The account to query for a previous name. 767 * @return The account's previous name, null if the account has never been 768 * renamed. 769 */ 770 public String getPreviousName(final Account account) { 771 if (account == null) throw new IllegalArgumentException("account is null"); 772 try { 773 return mService.getPreviousName(account); 774 } catch (RemoteException e) { 775 // will never happen 776 throw new RuntimeException(e); 777 } 778 } 779 780 /** 781 * Removes an account from the AccountManager. Does nothing if the account 782 * does not exist. Does not delete the account from the server. 783 * The authenticator may have its own policies preventing account 784 * deletion, in which case the account will not be deleted. 785 * 786 * <p>This method may be called from any thread, but the returned 787 * {@link AccountManagerFuture} must not be used on the main thread. 788 * 789 * <p>This method requires the caller to hold the permission 790 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 791 * 792 * @param account The {@link Account} to remove 793 * @param callback Callback to invoke when the request completes, 794 * null for no callback 795 * @param handler {@link Handler} identifying the callback thread, 796 * null for the main thread 797 * @return An {@link AccountManagerFuture} which resolves to a Boolean, 798 * true if the account has been successfully removed 799 * @deprecated use 800 * {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)} 801 * instead 802 */ 803 @RequiresPermission(MANAGE_ACCOUNTS) 804 @Deprecated 805 public AccountManagerFuture<Boolean> removeAccount(final Account account, 806 AccountManagerCallback<Boolean> callback, Handler handler) { 807 if (account == null) throw new IllegalArgumentException("account is null"); 808 return new Future2Task<Boolean>(handler, callback) { 809 public void doWork() throws RemoteException { 810 mService.removeAccount(mResponse, account, false); 811 } 812 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { 813 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { 814 throw new AuthenticatorException("no result in response"); 815 } 816 return bundle.getBoolean(KEY_BOOLEAN_RESULT); 817 } 818 }.start(); 819 } 820 821 /** 822 * Removes an account from the AccountManager. Does nothing if the account 823 * does not exist. Does not delete the account from the server. 824 * The authenticator may have its own policies preventing account 825 * deletion, in which case the account will not be deleted. 826 * 827 * <p>This method may be called from any thread, but the returned 828 * {@link AccountManagerFuture} must not be used on the main thread. 829 * 830 * <p>This method requires the caller to hold the permission 831 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 832 * 833 * @param account The {@link Account} to remove 834 * @param activity The {@link Activity} context to use for launching a new 835 * authenticator-defined sub-Activity to prompt the user to delete an 836 * account; used only to call startActivity(); if null, the prompt 837 * will not be launched directly, but the {@link Intent} may be 838 * returned to the caller instead 839 * @param callback Callback to invoke when the request completes, 840 * null for no callback 841 * @param handler {@link Handler} identifying the callback thread, 842 * null for the main thread 843 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 844 * {@link #KEY_BOOLEAN_RESULT} if activity was specified and an account 845 * was removed or if active. If no activity was specified, the returned 846 * Bundle contains only {@link #KEY_INTENT} with the {@link Intent} 847 * needed to launch the actual account removal process, if authenticator 848 * needs the activity launch. If an error occurred, 849 * {@link AccountManagerFuture#getResult()} throws: 850 * <ul> 851 * <li> {@link AuthenticatorException} if no authenticator was registered for 852 * this account type or the authenticator failed to respond 853 * <li> {@link OperationCanceledException} if the operation was canceled for 854 * any reason, including the user canceling the creation process or 855 * adding accounts (of this type) has been disabled by policy 856 * </ul> 857 */ 858 @RequiresPermission(MANAGE_ACCOUNTS) 859 public AccountManagerFuture<Bundle> removeAccount(final Account account, 860 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { 861 if (account == null) throw new IllegalArgumentException("account is null"); 862 return new AmsTask(activity, handler, callback) { 863 public void doWork() throws RemoteException { 864 mService.removeAccount(mResponse, account, activity != null); 865 } 866 }.start(); 867 } 868 869 /** 870 * @see #removeAccount(Account, AccountManagerCallback, Handler) 871 * @hide 872 * @deprecated use 873 * {@link #removeAccountAsUser(Account, Activity, AccountManagerCallback, Handler)} 874 * instead 875 */ 876 @Deprecated 877 public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account, 878 AccountManagerCallback<Boolean> callback, Handler handler, 879 final UserHandle userHandle) { 880 if (account == null) throw new IllegalArgumentException("account is null"); 881 if (userHandle == null) throw new IllegalArgumentException("userHandle is null"); 882 return new Future2Task<Boolean>(handler, callback) { 883 public void doWork() throws RemoteException { 884 mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier()); 885 } 886 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { 887 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { 888 throw new AuthenticatorException("no result in response"); 889 } 890 return bundle.getBoolean(KEY_BOOLEAN_RESULT); 891 } 892 }.start(); 893 } 894 895 /** 896 * @see #removeAccount(Account, Activity, AccountManagerCallback, Handler) 897 * @hide 898 */ 899 public AccountManagerFuture<Bundle> removeAccountAsUser(final Account account, 900 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler, 901 final UserHandle userHandle) { 902 if (account == null) 903 throw new IllegalArgumentException("account is null"); 904 if (userHandle == null) 905 throw new IllegalArgumentException("userHandle is null"); 906 return new AmsTask(activity, handler, callback) { 907 public void doWork() throws RemoteException { 908 mService.removeAccountAsUser(mResponse, account, activity != null, 909 userHandle.getIdentifier()); 910 } 911 }.start(); 912 } 913 914 /** 915 * Removes an account directly. Normally used by authenticators, not 916 * directly by applications. Does not delete the account from the server. 917 * The authenticator may have its own policies preventing account deletion, 918 * in which case the account will not be deleted. 919 * <p> 920 * It is safe to call this method from the main thread. 921 * <p> 922 * This method requires the caller to hold the permission 923 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and to have the 924 * same UID or signature as the account's authenticator. 925 * 926 * @param account The {@link Account} to delete. 927 * @return True if the account was successfully deleted, false if the 928 * account did not exist, the account is null, or another error 929 * occurs. 930 */ 931 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 932 public boolean removeAccountExplicitly(Account account) { 933 if (account == null) throw new IllegalArgumentException("account is null"); 934 try { 935 return mService.removeAccountExplicitly(account); 936 } catch (RemoteException e) { 937 // won't ever happen 938 throw new RuntimeException(e); 939 } 940 } 941 942 /** 943 * Removes an auth token from the AccountManager's cache. Does nothing if 944 * the auth token is not currently in the cache. Applications must call this 945 * method when the auth token is found to have expired or otherwise become 946 * invalid for authenticating requests. The AccountManager does not validate 947 * or expire cached auth tokens otherwise. 948 * 949 * <p>It is safe to call this method from the main thread. 950 * 951 * <p>This method requires the caller to hold the permission 952 * {@link android.Manifest.permission#MANAGE_ACCOUNTS} or 953 * {@link android.Manifest.permission#USE_CREDENTIALS} 954 * 955 * @param accountType The account type of the auth token to invalidate, must not be null 956 * @param authToken The auth token to invalidate, may be null 957 */ 958 @RequiresPermission(anyOf = {MANAGE_ACCOUNTS, USE_CREDENTIALS}) 959 public void invalidateAuthToken(final String accountType, final String authToken) { 960 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 961 try { 962 if (authToken != null) { 963 mService.invalidateAuthToken(accountType, authToken); 964 } 965 } catch (RemoteException e) { 966 // won't ever happen 967 throw new RuntimeException(e); 968 } 969 } 970 971 /** 972 * Gets an auth token from the AccountManager's cache. If no auth 973 * token is cached for this account, null will be returned -- a new 974 * auth token will not be generated, and the server will not be contacted. 975 * Intended for use by the authenticator, not directly by applications. 976 * 977 * <p>It is safe to call this method from the main thread. 978 * 979 * <p>This method requires the caller to hold the permission 980 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 981 * and to have the same UID as the account's authenticator. 982 * 983 * @param account The account to fetch an auth token for 984 * @param authTokenType The type of auth token to fetch, see {#getAuthToken} 985 * @return The cached auth token for this account and type, or null if 986 * no auth token is cached or the account does not exist. 987 */ 988 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 989 public String peekAuthToken(final Account account, final String authTokenType) { 990 if (account == null) throw new IllegalArgumentException("account is null"); 991 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 992 try { 993 return mService.peekAuthToken(account, authTokenType); 994 } catch (RemoteException e) { 995 // won't ever happen 996 throw new RuntimeException(e); 997 } 998 } 999 1000 /** 1001 * Sets or forgets a saved password. This modifies the local copy of the 1002 * password used to automatically authenticate the user; it does 1003 * not change the user's account password on the server. Intended for use 1004 * by the authenticator, not directly by applications. 1005 * 1006 * <p>It is safe to call this method from the main thread. 1007 * 1008 * <p>This method requires the caller to hold the permission 1009 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 1010 * and have the same UID as the account's authenticator. 1011 * 1012 * @param account The account to set a password for 1013 * @param password The password to set, null to clear the password 1014 */ 1015 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 1016 public void setPassword(final Account account, final String password) { 1017 if (account == null) throw new IllegalArgumentException("account is null"); 1018 try { 1019 mService.setPassword(account, password); 1020 } catch (RemoteException e) { 1021 // won't ever happen 1022 throw new RuntimeException(e); 1023 } 1024 } 1025 1026 /** 1027 * Forgets a saved password. This erases the local copy of the password; 1028 * it does not change the user's account password on the server. 1029 * Has the same effect as setPassword(account, null) but requires fewer 1030 * permissions, and may be used by applications or management interfaces 1031 * to "sign out" from an account. 1032 * 1033 * <p>It is safe to call this method from the main thread. 1034 * 1035 * <p>This method requires the caller to hold the permission 1036 * {@link android.Manifest.permission#MANAGE_ACCOUNTS} 1037 * 1038 * @param account The account whose password to clear 1039 */ 1040 @RequiresPermission(MANAGE_ACCOUNTS) 1041 public void clearPassword(final Account account) { 1042 if (account == null) throw new IllegalArgumentException("account is null"); 1043 try { 1044 mService.clearPassword(account); 1045 } catch (RemoteException e) { 1046 // won't ever happen 1047 throw new RuntimeException(e); 1048 } 1049 } 1050 1051 /** 1052 * Sets one userdata key for an account. Intended by use for the 1053 * authenticator to stash state for itself, not directly by applications. 1054 * The meaning of the keys and values is up to the authenticator. 1055 * 1056 * <p>It is safe to call this method from the main thread. 1057 * 1058 * <p>This method requires the caller to hold the permission 1059 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 1060 * and to have the same UID as the account's authenticator. 1061 * 1062 * @param account The account to set the userdata for 1063 * @param key The userdata key to set. Must not be null 1064 * @param value The value to set, null to clear this userdata key 1065 */ 1066 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 1067 public void setUserData(final Account account, final String key, final String value) { 1068 if (account == null) throw new IllegalArgumentException("account is null"); 1069 if (key == null) throw new IllegalArgumentException("key is null"); 1070 try { 1071 mService.setUserData(account, key, value); 1072 } catch (RemoteException e) { 1073 // won't ever happen 1074 throw new RuntimeException(e); 1075 } 1076 } 1077 1078 /** 1079 * Adds an auth token to the AccountManager cache for an account. 1080 * If the account does not exist then this call has no effect. 1081 * Replaces any previous auth token for this account and auth token type. 1082 * Intended for use by the authenticator, not directly by applications. 1083 * 1084 * <p>It is safe to call this method from the main thread. 1085 * 1086 * <p>This method requires the caller to hold the permission 1087 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} 1088 * and to have the same UID as the account's authenticator. 1089 * 1090 * @param account The account to set an auth token for 1091 * @param authTokenType The type of the auth token, see {#getAuthToken} 1092 * @param authToken The auth token to add to the cache 1093 */ 1094 @RequiresPermission(AUTHENTICATE_ACCOUNTS) 1095 public void setAuthToken(Account account, final String authTokenType, final String authToken) { 1096 if (account == null) throw new IllegalArgumentException("account is null"); 1097 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1098 try { 1099 mService.setAuthToken(account, authTokenType, authToken); 1100 } catch (RemoteException e) { 1101 // won't ever happen 1102 throw new RuntimeException(e); 1103 } 1104 } 1105 1106 /** 1107 * This convenience helper synchronously gets an auth token with 1108 * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}. 1109 * 1110 * <p>This method may block while a network request completes, and must 1111 * never be made from the main thread. 1112 * 1113 * <p>This method requires the caller to hold the permission 1114 * {@link android.Manifest.permission#USE_CREDENTIALS}. 1115 * 1116 * @param account The account to fetch an auth token for 1117 * @param authTokenType The auth token type, see {@link #getAuthToken getAuthToken()} 1118 * @param notifyAuthFailure If true, display a notification and return null 1119 * if authentication fails; if false, prompt and wait for the user to 1120 * re-enter correct credentials before returning 1121 * @return An auth token of the specified type for this account, or null 1122 * if authentication fails or none can be fetched. 1123 * @throws AuthenticatorException if the authenticator failed to respond 1124 * @throws OperationCanceledException if the request was canceled for any 1125 * reason, including the user canceling a credential request 1126 * @throws java.io.IOException if the authenticator experienced an I/O problem 1127 * creating a new auth token, usually because of network trouble 1128 */ 1129 @RequiresPermission(USE_CREDENTIALS) 1130 public String blockingGetAuthToken(Account account, String authTokenType, 1131 boolean notifyAuthFailure) 1132 throws OperationCanceledException, IOException, AuthenticatorException { 1133 if (account == null) throw new IllegalArgumentException("account is null"); 1134 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1135 Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */, 1136 null /* handler */).getResult(); 1137 if (bundle == null) { 1138 // This should never happen, but it does, occasionally. If it does return null to 1139 // signify that we were not able to get the authtoken. 1140 // TODO: remove this when the bug is found that sometimes causes a null bundle to be 1141 // returned 1142 Log.e(TAG, "blockingGetAuthToken: null was returned from getResult() for " 1143 + account + ", authTokenType " + authTokenType); 1144 return null; 1145 } 1146 return bundle.getString(KEY_AUTHTOKEN); 1147 } 1148 1149 /** 1150 * Gets an auth token of the specified type for a particular account, 1151 * prompting the user for credentials if necessary. This method is 1152 * intended for applications running in the foreground where it makes 1153 * sense to ask the user directly for a password. 1154 * 1155 * <p>If a previously generated auth token is cached for this account and 1156 * type, then it is returned. Otherwise, if a saved password is 1157 * available, it is sent to the server to generate a new auth token. 1158 * Otherwise, the user is prompted to enter a password. 1159 * 1160 * <p>Some authenticators have auth token <em>types</em>, whose value 1161 * is authenticator-dependent. Some services use different token types to 1162 * access different functionality -- for example, Google uses different auth 1163 * tokens to access Gmail and Google Calendar for the same account. 1164 * 1165 * <p>This method may be called from any thread, but the returned 1166 * {@link AccountManagerFuture} must not be used on the main thread. 1167 * 1168 * <p>This method requires the caller to hold the permission 1169 * {@link android.Manifest.permission#USE_CREDENTIALS}. 1170 * 1171 * @param account The account to fetch an auth token for 1172 * @param authTokenType The auth token type, an authenticator-dependent 1173 * string token, must not be null 1174 * @param options Authenticator-specific options for the request, 1175 * may be null or empty 1176 * @param activity The {@link Activity} context to use for launching a new 1177 * authenticator-defined sub-Activity to prompt the user for a password 1178 * if necessary; used only to call startActivity(); must not be null. 1179 * @param callback Callback to invoke when the request completes, 1180 * null for no callback 1181 * @param handler {@link Handler} identifying the callback thread, 1182 * null for the main thread 1183 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 1184 * at least the following fields: 1185 * <ul> 1186 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied 1187 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1188 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted 1189 * </ul> 1190 * 1191 * (Other authenticator-specific values may be returned.) If an auth token 1192 * could not be fetched, {@link AccountManagerFuture#getResult()} throws: 1193 * <ul> 1194 * <li> {@link AuthenticatorException} if the authenticator failed to respond 1195 * <li> {@link OperationCanceledException} if the operation is canceled for 1196 * any reason, incluidng the user canceling a credential request 1197 * <li> {@link IOException} if the authenticator experienced an I/O problem 1198 * creating a new auth token, usually because of network trouble 1199 * </ul> 1200 * If the account is no longer present on the device, the return value is 1201 * authenticator-dependent. The caller should verify the validity of the 1202 * account before requesting an auth token. 1203 */ 1204 @RequiresPermission(USE_CREDENTIALS) 1205 public AccountManagerFuture<Bundle> getAuthToken( 1206 final Account account, final String authTokenType, final Bundle options, 1207 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { 1208 if (account == null) throw new IllegalArgumentException("account is null"); 1209 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1210 final Bundle optionsIn = new Bundle(); 1211 if (options != null) { 1212 optionsIn.putAll(options); 1213 } 1214 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); 1215 return new AmsTask(activity, handler, callback) { 1216 public void doWork() throws RemoteException { 1217 mService.getAuthToken(mResponse, account, authTokenType, 1218 false /* notifyOnAuthFailure */, true /* expectActivityLaunch */, 1219 optionsIn); 1220 } 1221 }.start(); 1222 } 1223 1224 /** 1225 * Gets an auth token of the specified type for a particular account, 1226 * optionally raising a notification if the user must enter credentials. 1227 * This method is intended for background tasks and services where the 1228 * user should not be immediately interrupted with a password prompt. 1229 * 1230 * <p>If a previously generated auth token is cached for this account and 1231 * type, then it is returned. Otherwise, if a saved password is 1232 * available, it is sent to the server to generate a new auth token. 1233 * Otherwise, an {@link Intent} is returned which, when started, will 1234 * prompt the user for a password. If the notifyAuthFailure parameter is 1235 * set, a status bar notification is also created with the same Intent, 1236 * alerting the user that they need to enter a password at some point. 1237 * 1238 * <p>In that case, you may need to wait until the user responds, which 1239 * could take hours or days or forever. When the user does respond and 1240 * supply a new password, the account manager will broadcast the 1241 * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can 1242 * use to try again. 1243 * 1244 * <p>If notifyAuthFailure is not set, it is the application's 1245 * responsibility to launch the returned Intent at some point. 1246 * Either way, the result from this call will not wait for user action. 1247 * 1248 * <p>Some authenticators have auth token <em>types</em>, whose value 1249 * is authenticator-dependent. Some services use different token types to 1250 * access different functionality -- for example, Google uses different auth 1251 * tokens to access Gmail and Google Calendar for the same account. 1252 * 1253 * <p>This method may be called from any thread, but the returned 1254 * {@link AccountManagerFuture} must not be used on the main thread. 1255 * 1256 * <p>This method requires the caller to hold the permission 1257 * {@link android.Manifest.permission#USE_CREDENTIALS}. 1258 * 1259 * @param account The account to fetch an auth token for 1260 * @param authTokenType The auth token type, an authenticator-dependent 1261 * string token, must not be null 1262 * @param notifyAuthFailure True to add a notification to prompt the 1263 * user for a password if necessary, false to leave that to the caller 1264 * @param callback Callback to invoke when the request completes, 1265 * null for no callback 1266 * @param handler {@link Handler} identifying the callback thread, 1267 * null for the main thread 1268 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 1269 * at least the following fields on success: 1270 * <ul> 1271 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied 1272 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1273 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted 1274 * </ul> 1275 * 1276 * (Other authenticator-specific values may be returned.) If the user 1277 * must enter credentials, the returned Bundle contains only 1278 * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt. 1279 * 1280 * If an error occurred, {@link AccountManagerFuture#getResult()} throws: 1281 * <ul> 1282 * <li> {@link AuthenticatorException} if the authenticator failed to respond 1283 * <li> {@link OperationCanceledException} if the operation is canceled for 1284 * any reason, incluidng the user canceling a credential request 1285 * <li> {@link IOException} if the authenticator experienced an I/O problem 1286 * creating a new auth token, usually because of network trouble 1287 * </ul> 1288 * If the account is no longer present on the device, the return value is 1289 * authenticator-dependent. The caller should verify the validity of the 1290 * account before requesting an auth token. 1291 * @deprecated use {@link #getAuthToken(Account, String, android.os.Bundle, 1292 * boolean, AccountManagerCallback, android.os.Handler)} instead 1293 */ 1294 @Deprecated 1295 @RequiresPermission(USE_CREDENTIALS) 1296 public AccountManagerFuture<Bundle> getAuthToken( 1297 final Account account, final String authTokenType, 1298 final boolean notifyAuthFailure, 1299 AccountManagerCallback<Bundle> callback, Handler handler) { 1300 return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback, 1301 handler); 1302 } 1303 1304 /** 1305 * Gets an auth token of the specified type for a particular account, 1306 * optionally raising a notification if the user must enter credentials. 1307 * This method is intended for background tasks and services where the 1308 * user should not be immediately interrupted with a password prompt. 1309 * 1310 * <p>If a previously generated auth token is cached for this account and 1311 * type, then it is returned. Otherwise, if a saved password is 1312 * available, it is sent to the server to generate a new auth token. 1313 * Otherwise, an {@link Intent} is returned which, when started, will 1314 * prompt the user for a password. If the notifyAuthFailure parameter is 1315 * set, a status bar notification is also created with the same Intent, 1316 * alerting the user that they need to enter a password at some point. 1317 * 1318 * <p>In that case, you may need to wait until the user responds, which 1319 * could take hours or days or forever. When the user does respond and 1320 * supply a new password, the account manager will broadcast the 1321 * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can 1322 * use to try again. 1323 * 1324 * <p>If notifyAuthFailure is not set, it is the application's 1325 * responsibility to launch the returned Intent at some point. 1326 * Either way, the result from this call will not wait for user action. 1327 * 1328 * <p>Some authenticators have auth token <em>types</em>, whose value 1329 * is authenticator-dependent. Some services use different token types to 1330 * access different functionality -- for example, Google uses different auth 1331 * tokens to access Gmail and Google Calendar for the same account. 1332 * 1333 * <p>This method may be called from any thread, but the returned 1334 * {@link AccountManagerFuture} must not be used on the main thread. 1335 * 1336 * <p>This method requires the caller to hold the permission 1337 * {@link android.Manifest.permission#USE_CREDENTIALS}. 1338 * 1339 * @param account The account to fetch an auth token for 1340 * @param authTokenType The auth token type, an authenticator-dependent 1341 * string token, must not be null 1342 * @param options Authenticator-specific options for the request, 1343 * may be null or empty 1344 * @param notifyAuthFailure True to add a notification to prompt the 1345 * user for a password if necessary, false to leave that to the caller 1346 * @param callback Callback to invoke when the request completes, 1347 * null for no callback 1348 * @param handler {@link Handler} identifying the callback thread, 1349 * null for the main thread 1350 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 1351 * at least the following fields on success: 1352 * <ul> 1353 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied 1354 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1355 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted 1356 * </ul> 1357 * 1358 * (Other authenticator-specific values may be returned.) If the user 1359 * must enter credentials, the returned Bundle contains only 1360 * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt. 1361 * 1362 * If an error occurred, {@link AccountManagerFuture#getResult()} throws: 1363 * <ul> 1364 * <li> {@link AuthenticatorException} if the authenticator failed to respond 1365 * <li> {@link OperationCanceledException} if the operation is canceled for 1366 * any reason, incluidng the user canceling a credential request 1367 * <li> {@link IOException} if the authenticator experienced an I/O problem 1368 * creating a new auth token, usually because of network trouble 1369 * </ul> 1370 * If the account is no longer present on the device, the return value is 1371 * authenticator-dependent. The caller should verify the validity of the 1372 * account before requesting an auth token. 1373 */ 1374 @RequiresPermission(USE_CREDENTIALS) 1375 public AccountManagerFuture<Bundle> getAuthToken( 1376 final Account account, final String authTokenType, final Bundle options, 1377 final boolean notifyAuthFailure, 1378 AccountManagerCallback<Bundle> callback, Handler handler) { 1379 1380 if (account == null) throw new IllegalArgumentException("account is null"); 1381 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1382 final Bundle optionsIn = new Bundle(); 1383 if (options != null) { 1384 optionsIn.putAll(options); 1385 } 1386 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); 1387 return new AmsTask(null, handler, callback) { 1388 public void doWork() throws RemoteException { 1389 mService.getAuthToken(mResponse, account, authTokenType, 1390 notifyAuthFailure, false /* expectActivityLaunch */, optionsIn); 1391 } 1392 }.start(); 1393 } 1394 1395 /** 1396 * Asks the user to add an account of a specified type. The authenticator 1397 * for this account type processes this request with the appropriate user 1398 * interface. If the user does elect to create a new account, the account 1399 * name is returned. 1400 * 1401 * <p>This method may be called from any thread, but the returned 1402 * {@link AccountManagerFuture} must not be used on the main thread. 1403 * 1404 * <p>This method requires the caller to hold the permission 1405 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 1406 * 1407 * @param accountType The type of account to add; must not be null 1408 * @param authTokenType The type of auth token (see {@link #getAuthToken}) 1409 * this account will need to be able to generate, null for none 1410 * @param requiredFeatures The features (see {@link #hasFeatures}) this 1411 * account must have, null for none 1412 * @param addAccountOptions Authenticator-specific options for the request, 1413 * may be null or empty 1414 * @param activity The {@link Activity} context to use for launching a new 1415 * authenticator-defined sub-Activity to prompt the user to create an 1416 * account; used only to call startActivity(); if null, the prompt 1417 * will not be launched directly, but the necessary {@link Intent} 1418 * will be returned to the caller instead 1419 * @param callback Callback to invoke when the request completes, 1420 * null for no callback 1421 * @param handler {@link Handler} identifying the callback thread, 1422 * null for the main thread 1423 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 1424 * these fields if activity was specified and an account was created: 1425 * <ul> 1426 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created 1427 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1428 * </ul> 1429 * 1430 * If no activity was specified, the returned Bundle contains only 1431 * {@link #KEY_INTENT} with the {@link Intent} needed to launch the 1432 * actual account creation process. If an error occurred, 1433 * {@link AccountManagerFuture#getResult()} throws: 1434 * <ul> 1435 * <li> {@link AuthenticatorException} if no authenticator was registered for 1436 * this account type or the authenticator failed to respond 1437 * <li> {@link OperationCanceledException} if the operation was canceled for 1438 * any reason, including the user canceling the creation process or adding accounts 1439 * (of this type) has been disabled by policy 1440 * <li> {@link IOException} if the authenticator experienced an I/O problem 1441 * creating a new account, usually because of network trouble 1442 * </ul> 1443 */ 1444 @RequiresPermission(MANAGE_ACCOUNTS) 1445 public AccountManagerFuture<Bundle> addAccount(final String accountType, 1446 final String authTokenType, final String[] requiredFeatures, 1447 final Bundle addAccountOptions, 1448 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { 1449 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1450 final Bundle optionsIn = new Bundle(); 1451 if (addAccountOptions != null) { 1452 optionsIn.putAll(addAccountOptions); 1453 } 1454 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); 1455 1456 return new AmsTask(activity, handler, callback) { 1457 public void doWork() throws RemoteException { 1458 mService.addAccount(mResponse, accountType, authTokenType, 1459 requiredFeatures, activity != null, optionsIn); 1460 } 1461 }.start(); 1462 } 1463 1464 /** 1465 * @see #addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback, Handler) 1466 * @hide 1467 */ 1468 public AccountManagerFuture<Bundle> addAccountAsUser(final String accountType, 1469 final String authTokenType, final String[] requiredFeatures, 1470 final Bundle addAccountOptions, final Activity activity, 1471 AccountManagerCallback<Bundle> callback, Handler handler, final UserHandle userHandle) { 1472 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1473 if (userHandle == null) throw new IllegalArgumentException("userHandle is null"); 1474 final Bundle optionsIn = new Bundle(); 1475 if (addAccountOptions != null) { 1476 optionsIn.putAll(addAccountOptions); 1477 } 1478 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); 1479 1480 return new AmsTask(activity, handler, callback) { 1481 public void doWork() throws RemoteException { 1482 mService.addAccountAsUser(mResponse, accountType, authTokenType, 1483 requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier()); 1484 } 1485 }.start(); 1486 } 1487 1488 /** 1489 * Adds a shared account from the primary user to a secondary user. Adding the shared account 1490 * doesn't take effect immediately. When the target user starts up, any pending shared accounts 1491 * are attempted to be copied to the target user from the primary via calls to the 1492 * authenticator. 1493 * @param account the account to share 1494 * @param user the target user 1495 * @return 1496 * @hide 1497 */ 1498 public boolean addSharedAccount(final Account account, UserHandle user) { 1499 try { 1500 boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier()); 1501 return val; 1502 } catch (RemoteException re) { 1503 // won't ever happen 1504 throw new RuntimeException(re); 1505 } 1506 } 1507 1508 /** 1509 * Copies an account from the primary user to another user. 1510 * @param account the account to copy 1511 * @param user the target user 1512 * @param callback Callback to invoke when the request completes, 1513 * null for no callback 1514 * @param handler {@link Handler} identifying the callback thread, 1515 * null for the main thread 1516 * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it 1517 * succeeded. 1518 * @hide 1519 */ 1520 public AccountManagerFuture<Boolean> copyAccountToUser( 1521 final Account account, final UserHandle user, 1522 AccountManagerCallback<Boolean> callback, Handler handler) { 1523 if (account == null) throw new IllegalArgumentException("account is null"); 1524 if (user == null) throw new IllegalArgumentException("user is null"); 1525 1526 return new Future2Task<Boolean>(handler, callback) { 1527 @Override 1528 public void doWork() throws RemoteException { 1529 mService.copyAccountToUser( 1530 mResponse, account, UserHandle.USER_OWNER, user.getIdentifier()); 1531 } 1532 @Override 1533 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { 1534 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { 1535 throw new AuthenticatorException("no result in response"); 1536 } 1537 return bundle.getBoolean(KEY_BOOLEAN_RESULT); 1538 } 1539 }.start(); 1540 } 1541 1542 /** 1543 * @hide 1544 * Removes the shared account. 1545 * @param account the account to remove 1546 * @param user the user to remove the account from 1547 * @return 1548 */ 1549 public boolean removeSharedAccount(final Account account, UserHandle user) { 1550 try { 1551 boolean val = mService.removeSharedAccountAsUser(account, user.getIdentifier()); 1552 return val; 1553 } catch (RemoteException re) { 1554 // won't ever happen 1555 throw new RuntimeException(re); 1556 } 1557 } 1558 1559 /** 1560 * @hide 1561 * @param user 1562 * @return 1563 */ 1564 public Account[] getSharedAccounts(UserHandle user) { 1565 try { 1566 return mService.getSharedAccountsAsUser(user.getIdentifier()); 1567 } catch (RemoteException re) { 1568 // won't ever happen 1569 throw new RuntimeException(re); 1570 } 1571 } 1572 1573 /** 1574 * Confirms that the user knows the password for an account to make extra 1575 * sure they are the owner of the account. The user-entered password can 1576 * be supplied directly, otherwise the authenticator for this account type 1577 * prompts the user with the appropriate interface. This method is 1578 * intended for applications which want extra assurance; for example, the 1579 * phone lock screen uses this to let the user unlock the phone with an 1580 * account password if they forget the lock pattern. 1581 * 1582 * <p>If the user-entered password matches a saved password for this 1583 * account, the request is considered valid; otherwise the authenticator 1584 * verifies the password (usually by contacting the server). 1585 * 1586 * <p>This method may be called from any thread, but the returned 1587 * {@link AccountManagerFuture} must not be used on the main thread. 1588 * 1589 * <p>This method requires the caller to hold the permission 1590 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 1591 * 1592 * @param account The account to confirm password knowledge for 1593 * @param options Authenticator-specific options for the request; 1594 * if the {@link #KEY_PASSWORD} string field is present, the 1595 * authenticator may use it directly rather than prompting the user; 1596 * may be null or empty 1597 * @param activity The {@link Activity} context to use for launching a new 1598 * authenticator-defined sub-Activity to prompt the user to enter a 1599 * password; used only to call startActivity(); if null, the prompt 1600 * will not be launched directly, but the necessary {@link Intent} 1601 * will be returned to the caller instead 1602 * @param callback Callback to invoke when the request completes, 1603 * null for no callback 1604 * @param handler {@link Handler} identifying the callback thread, 1605 * null for the main thread 1606 * @return An {@link AccountManagerFuture} which resolves to a Bundle 1607 * with these fields if activity or password was supplied and 1608 * the account was successfully verified: 1609 * <ul> 1610 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account verified 1611 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1612 * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success 1613 * </ul> 1614 * 1615 * If no activity or password was specified, the returned Bundle contains 1616 * {@link #KEY_INTENT} with the {@link Intent} needed to launch the 1617 * password prompt. 1618 * 1619 * <p>Also the returning Bundle may contain {@link 1620 * #KEY_LAST_AUTHENTICATED_TIME} indicating the last time the 1621 * credential was validated/created. 1622 * 1623 * If an error occurred,{@link AccountManagerFuture#getResult()} throws: 1624 * <ul> 1625 * <li> {@link AuthenticatorException} if the authenticator failed to respond 1626 * <li> {@link OperationCanceledException} if the operation was canceled for 1627 * any reason, including the user canceling the password prompt 1628 * <li> {@link IOException} if the authenticator experienced an I/O problem 1629 * verifying the password, usually because of network trouble 1630 * </ul> 1631 */ 1632 @RequiresPermission(MANAGE_ACCOUNTS) 1633 public AccountManagerFuture<Bundle> confirmCredentials(final Account account, 1634 final Bundle options, 1635 final Activity activity, 1636 final AccountManagerCallback<Bundle> callback, 1637 final Handler handler) { 1638 return confirmCredentialsAsUser(account, options, activity, callback, handler, 1639 Process.myUserHandle()); 1640 } 1641 1642 /** 1643 * @hide 1644 * Same as {@link #confirmCredentials(Account, Bundle, Activity, AccountManagerCallback, Handler)} 1645 * but for the specified user. 1646 */ 1647 public AccountManagerFuture<Bundle> confirmCredentialsAsUser(final Account account, 1648 final Bundle options, 1649 final Activity activity, 1650 final AccountManagerCallback<Bundle> callback, 1651 final Handler handler, UserHandle userHandle) { 1652 if (account == null) throw new IllegalArgumentException("account is null"); 1653 final int userId = userHandle.getIdentifier(); 1654 return new AmsTask(activity, handler, callback) { 1655 public void doWork() throws RemoteException { 1656 mService.confirmCredentialsAsUser(mResponse, account, options, activity != null, 1657 userId); 1658 } 1659 }.start(); 1660 } 1661 1662 /** 1663 * Asks the user to enter a new password for an account, updating the 1664 * saved credentials for the account. Normally this happens automatically 1665 * when the server rejects credentials during an auth token fetch, but this 1666 * can be invoked directly to ensure we have the correct credentials stored. 1667 * 1668 * <p>This method may be called from any thread, but the returned 1669 * {@link AccountManagerFuture} must not be used on the main thread. 1670 * 1671 * <p>This method requires the caller to hold the permission 1672 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 1673 * 1674 * @param account The account to update credentials for 1675 * @param authTokenType The credentials entered must allow an auth token 1676 * of this type to be created (but no actual auth token is returned); 1677 * may be null 1678 * @param options Authenticator-specific options for the request; 1679 * may be null or empty 1680 * @param activity The {@link Activity} context to use for launching a new 1681 * authenticator-defined sub-Activity to prompt the user to enter a 1682 * password; used only to call startActivity(); if null, the prompt 1683 * will not be launched directly, but the necessary {@link Intent} 1684 * will be returned to the caller instead 1685 * @param callback Callback to invoke when the request completes, 1686 * null for no callback 1687 * @param handler {@link Handler} identifying the callback thread, 1688 * null for the main thread 1689 * @return An {@link AccountManagerFuture} which resolves to a Bundle 1690 * with these fields if an activity was supplied and the account 1691 * credentials were successfully updated: 1692 * <ul> 1693 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created 1694 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 1695 * </ul> 1696 * 1697 * If no activity was specified, the returned Bundle contains 1698 * {@link #KEY_INTENT} with the {@link Intent} needed to launch the 1699 * password prompt. If an error occurred, 1700 * {@link AccountManagerFuture#getResult()} throws: 1701 * <ul> 1702 * <li> {@link AuthenticatorException} if the authenticator failed to respond 1703 * <li> {@link OperationCanceledException} if the operation was canceled for 1704 * any reason, including the user canceling the password prompt 1705 * <li> {@link IOException} if the authenticator experienced an I/O problem 1706 * verifying the password, usually because of network trouble 1707 * </ul> 1708 */ 1709 @RequiresPermission(MANAGE_ACCOUNTS) 1710 public AccountManagerFuture<Bundle> updateCredentials(final Account account, 1711 final String authTokenType, 1712 final Bundle options, final Activity activity, 1713 final AccountManagerCallback<Bundle> callback, 1714 final Handler handler) { 1715 if (account == null) throw new IllegalArgumentException("account is null"); 1716 return new AmsTask(activity, handler, callback) { 1717 public void doWork() throws RemoteException { 1718 mService.updateCredentials(mResponse, account, authTokenType, activity != null, 1719 options); 1720 } 1721 }.start(); 1722 } 1723 1724 /** 1725 * Offers the user an opportunity to change an authenticator's settings. 1726 * These properties are for the authenticator in general, not a particular 1727 * account. Not all authenticators support this method. 1728 * 1729 * <p>This method may be called from any thread, but the returned 1730 * {@link AccountManagerFuture} must not be used on the main thread. 1731 * 1732 * <p>This method requires the caller to hold the permission 1733 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 1734 * 1735 * @param accountType The account type associated with the authenticator 1736 * to adjust 1737 * @param activity The {@link Activity} context to use for launching a new 1738 * authenticator-defined sub-Activity to adjust authenticator settings; 1739 * used only to call startActivity(); if null, the settings dialog will 1740 * not be launched directly, but the necessary {@link Intent} will be 1741 * returned to the caller instead 1742 * @param callback Callback to invoke when the request completes, 1743 * null for no callback 1744 * @param handler {@link Handler} identifying the callback thread, 1745 * null for the main thread 1746 * @return An {@link AccountManagerFuture} which resolves to a Bundle 1747 * which is empty if properties were edited successfully, or 1748 * if no activity was specified, contains only {@link #KEY_INTENT} 1749 * needed to launch the authenticator's settings dialog. 1750 * If an error occurred, {@link AccountManagerFuture#getResult()} 1751 * throws: 1752 * <ul> 1753 * <li> {@link AuthenticatorException} if no authenticator was registered for 1754 * this account type or the authenticator failed to respond 1755 * <li> {@link OperationCanceledException} if the operation was canceled for 1756 * any reason, including the user canceling the settings dialog 1757 * <li> {@link IOException} if the authenticator experienced an I/O problem 1758 * updating settings, usually because of network trouble 1759 * </ul> 1760 */ 1761 @RequiresPermission(MANAGE_ACCOUNTS) 1762 public AccountManagerFuture<Bundle> editProperties(final String accountType, 1763 final Activity activity, final AccountManagerCallback<Bundle> callback, 1764 final Handler handler) { 1765 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1766 return new AmsTask(activity, handler, callback) { 1767 public void doWork() throws RemoteException { 1768 mService.editProperties(mResponse, accountType, activity != null); 1769 } 1770 }.start(); 1771 } 1772 1773 private void ensureNotOnMainThread() { 1774 final Looper looper = Looper.myLooper(); 1775 if (looper != null && looper == mContext.getMainLooper()) { 1776 final IllegalStateException exception = new IllegalStateException( 1777 "calling this from your main thread can lead to deadlock"); 1778 Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs", 1779 exception); 1780 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) { 1781 throw exception; 1782 } 1783 } 1784 } 1785 1786 private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback, 1787 final AccountManagerFuture<Bundle> future) { 1788 handler = handler == null ? mMainHandler : handler; 1789 handler.post(new Runnable() { 1790 public void run() { 1791 callback.run(future); 1792 } 1793 }); 1794 } 1795 1796 private void postToHandler(Handler handler, final OnAccountsUpdateListener listener, 1797 final Account[] accounts) { 1798 final Account[] accountsCopy = new Account[accounts.length]; 1799 // send a copy to make sure that one doesn't 1800 // change what another sees 1801 System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length); 1802 handler = (handler == null) ? mMainHandler : handler; 1803 handler.post(new Runnable() { 1804 public void run() { 1805 try { 1806 listener.onAccountsUpdated(accountsCopy); 1807 } catch (SQLException e) { 1808 // Better luck next time. If the problem was disk-full, 1809 // the STORAGE_OK intent will re-trigger the update. 1810 Log.e(TAG, "Can't update accounts", e); 1811 } 1812 } 1813 }); 1814 } 1815 1816 private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> { 1817 final IAccountManagerResponse mResponse; 1818 final Handler mHandler; 1819 final AccountManagerCallback<Bundle> mCallback; 1820 final Activity mActivity; 1821 public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) { 1822 super(new Callable<Bundle>() { 1823 public Bundle call() throws Exception { 1824 throw new IllegalStateException("this should never be called"); 1825 } 1826 }); 1827 1828 mHandler = handler; 1829 mCallback = callback; 1830 mActivity = activity; 1831 mResponse = new Response(); 1832 } 1833 1834 public final AccountManagerFuture<Bundle> start() { 1835 try { 1836 doWork(); 1837 } catch (RemoteException e) { 1838 setException(e); 1839 } 1840 return this; 1841 } 1842 1843 protected void set(Bundle bundle) { 1844 // TODO: somehow a null is being set as the result of the Future. Log this 1845 // case to help debug where this is occurring. When this bug is fixed this 1846 // condition statement should be removed. 1847 if (bundle == null) { 1848 Log.e(TAG, "the bundle must not be null", new Exception()); 1849 } 1850 super.set(bundle); 1851 } 1852 1853 public abstract void doWork() throws RemoteException; 1854 1855 private Bundle internalGetResult(Long timeout, TimeUnit unit) 1856 throws OperationCanceledException, IOException, AuthenticatorException { 1857 if (!isDone()) { 1858 ensureNotOnMainThread(); 1859 } 1860 try { 1861 if (timeout == null) { 1862 return get(); 1863 } else { 1864 return get(timeout, unit); 1865 } 1866 } catch (CancellationException e) { 1867 throw new OperationCanceledException(); 1868 } catch (TimeoutException e) { 1869 // fall through and cancel 1870 } catch (InterruptedException e) { 1871 // fall through and cancel 1872 } catch (ExecutionException e) { 1873 final Throwable cause = e.getCause(); 1874 if (cause instanceof IOException) { 1875 throw (IOException) cause; 1876 } else if (cause instanceof UnsupportedOperationException) { 1877 throw new AuthenticatorException(cause); 1878 } else if (cause instanceof AuthenticatorException) { 1879 throw (AuthenticatorException) cause; 1880 } else if (cause instanceof RuntimeException) { 1881 throw (RuntimeException) cause; 1882 } else if (cause instanceof Error) { 1883 throw (Error) cause; 1884 } else { 1885 throw new IllegalStateException(cause); 1886 } 1887 } finally { 1888 cancel(true /* interrupt if running */); 1889 } 1890 throw new OperationCanceledException(); 1891 } 1892 1893 public Bundle getResult() 1894 throws OperationCanceledException, IOException, AuthenticatorException { 1895 return internalGetResult(null, null); 1896 } 1897 1898 public Bundle getResult(long timeout, TimeUnit unit) 1899 throws OperationCanceledException, IOException, AuthenticatorException { 1900 return internalGetResult(timeout, unit); 1901 } 1902 1903 protected void done() { 1904 if (mCallback != null) { 1905 postToHandler(mHandler, mCallback, this); 1906 } 1907 } 1908 1909 /** Handles the responses from the AccountManager */ 1910 private class Response extends IAccountManagerResponse.Stub { 1911 public void onResult(Bundle bundle) { 1912 Intent intent = bundle.getParcelable(KEY_INTENT); 1913 if (intent != null && mActivity != null) { 1914 // since the user provided an Activity we will silently start intents 1915 // that we see 1916 mActivity.startActivity(intent); 1917 // leave the Future running to wait for the real response to this request 1918 } else if (bundle.getBoolean("retry")) { 1919 try { 1920 doWork(); 1921 } catch (RemoteException e) { 1922 // this will only happen if the system process is dead, which means 1923 // we will be dying ourselves 1924 } 1925 } else { 1926 set(bundle); 1927 } 1928 } 1929 1930 public void onError(int code, String message) { 1931 if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED 1932 || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) { 1933 // the authenticator indicated that this request was canceled or we were 1934 // forbidden to fulfill; cancel now 1935 cancel(true /* mayInterruptIfRunning */); 1936 return; 1937 } 1938 setException(convertErrorToException(code, message)); 1939 } 1940 } 1941 1942 } 1943 1944 private abstract class BaseFutureTask<T> extends FutureTask<T> { 1945 final public IAccountManagerResponse mResponse; 1946 final Handler mHandler; 1947 1948 public BaseFutureTask(Handler handler) { 1949 super(new Callable<T>() { 1950 public T call() throws Exception { 1951 throw new IllegalStateException("this should never be called"); 1952 } 1953 }); 1954 mHandler = handler; 1955 mResponse = new Response(); 1956 } 1957 1958 public abstract void doWork() throws RemoteException; 1959 1960 public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException; 1961 1962 protected void postRunnableToHandler(Runnable runnable) { 1963 Handler handler = (mHandler == null) ? mMainHandler : mHandler; 1964 handler.post(runnable); 1965 } 1966 1967 protected void startTask() { 1968 try { 1969 doWork(); 1970 } catch (RemoteException e) { 1971 setException(e); 1972 } 1973 } 1974 1975 protected class Response extends IAccountManagerResponse.Stub { 1976 public void onResult(Bundle bundle) { 1977 try { 1978 T result = bundleToResult(bundle); 1979 if (result == null) { 1980 return; 1981 } 1982 set(result); 1983 return; 1984 } catch (ClassCastException e) { 1985 // we will set the exception below 1986 } catch (AuthenticatorException e) { 1987 // we will set the exception below 1988 } 1989 onError(ERROR_CODE_INVALID_RESPONSE, "no result in response"); 1990 } 1991 1992 public void onError(int code, String message) { 1993 if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED 1994 || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) { 1995 // the authenticator indicated that this request was canceled or we were 1996 // forbidden to fulfill; cancel now 1997 cancel(true /* mayInterruptIfRunning */); 1998 return; 1999 } 2000 setException(convertErrorToException(code, message)); 2001 } 2002 } 2003 } 2004 2005 private abstract class Future2Task<T> 2006 extends BaseFutureTask<T> implements AccountManagerFuture<T> { 2007 final AccountManagerCallback<T> mCallback; 2008 public Future2Task(Handler handler, AccountManagerCallback<T> callback) { 2009 super(handler); 2010 mCallback = callback; 2011 } 2012 2013 protected void done() { 2014 if (mCallback != null) { 2015 postRunnableToHandler(new Runnable() { 2016 public void run() { 2017 mCallback.run(Future2Task.this); 2018 } 2019 }); 2020 } 2021 } 2022 2023 public Future2Task<T> start() { 2024 startTask(); 2025 return this; 2026 } 2027 2028 private T internalGetResult(Long timeout, TimeUnit unit) 2029 throws OperationCanceledException, IOException, AuthenticatorException { 2030 if (!isDone()) { 2031 ensureNotOnMainThread(); 2032 } 2033 try { 2034 if (timeout == null) { 2035 return get(); 2036 } else { 2037 return get(timeout, unit); 2038 } 2039 } catch (InterruptedException e) { 2040 // fall through and cancel 2041 } catch (TimeoutException e) { 2042 // fall through and cancel 2043 } catch (CancellationException e) { 2044 // fall through and cancel 2045 } catch (ExecutionException e) { 2046 final Throwable cause = e.getCause(); 2047 if (cause instanceof IOException) { 2048 throw (IOException) cause; 2049 } else if (cause instanceof UnsupportedOperationException) { 2050 throw new AuthenticatorException(cause); 2051 } else if (cause instanceof AuthenticatorException) { 2052 throw (AuthenticatorException) cause; 2053 } else if (cause instanceof RuntimeException) { 2054 throw (RuntimeException) cause; 2055 } else if (cause instanceof Error) { 2056 throw (Error) cause; 2057 } else { 2058 throw new IllegalStateException(cause); 2059 } 2060 } finally { 2061 cancel(true /* interrupt if running */); 2062 } 2063 throw new OperationCanceledException(); 2064 } 2065 2066 public T getResult() 2067 throws OperationCanceledException, IOException, AuthenticatorException { 2068 return internalGetResult(null, null); 2069 } 2070 2071 public T getResult(long timeout, TimeUnit unit) 2072 throws OperationCanceledException, IOException, AuthenticatorException { 2073 return internalGetResult(timeout, unit); 2074 } 2075 2076 } 2077 2078 private Exception convertErrorToException(int code, String message) { 2079 if (code == ERROR_CODE_NETWORK_ERROR) { 2080 return new IOException(message); 2081 } 2082 2083 if (code == ERROR_CODE_UNSUPPORTED_OPERATION) { 2084 return new UnsupportedOperationException(message); 2085 } 2086 2087 if (code == ERROR_CODE_INVALID_RESPONSE) { 2088 return new AuthenticatorException(message); 2089 } 2090 2091 if (code == ERROR_CODE_BAD_ARGUMENTS) { 2092 return new IllegalArgumentException(message); 2093 } 2094 2095 return new AuthenticatorException(message); 2096 } 2097 2098 private class GetAuthTokenByTypeAndFeaturesTask 2099 extends AmsTask implements AccountManagerCallback<Bundle> { 2100 GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType, 2101 final String[] features, Activity activityForPrompting, 2102 final Bundle addAccountOptions, final Bundle loginOptions, 2103 AccountManagerCallback<Bundle> callback, Handler handler) { 2104 super(activityForPrompting, handler, callback); 2105 if (accountType == null) throw new IllegalArgumentException("account type is null"); 2106 mAccountType = accountType; 2107 mAuthTokenType = authTokenType; 2108 mFeatures = features; 2109 mAddAccountOptions = addAccountOptions; 2110 mLoginOptions = loginOptions; 2111 mMyCallback = this; 2112 } 2113 volatile AccountManagerFuture<Bundle> mFuture = null; 2114 final String mAccountType; 2115 final String mAuthTokenType; 2116 final String[] mFeatures; 2117 final Bundle mAddAccountOptions; 2118 final Bundle mLoginOptions; 2119 final AccountManagerCallback<Bundle> mMyCallback; 2120 private volatile int mNumAccounts = 0; 2121 2122 public void doWork() throws RemoteException { 2123 getAccountsByTypeAndFeatures(mAccountType, mFeatures, 2124 new AccountManagerCallback<Account[]>() { 2125 public void run(AccountManagerFuture<Account[]> future) { 2126 Account[] accounts; 2127 try { 2128 accounts = future.getResult(); 2129 } catch (OperationCanceledException e) { 2130 setException(e); 2131 return; 2132 } catch (IOException e) { 2133 setException(e); 2134 return; 2135 } catch (AuthenticatorException e) { 2136 setException(e); 2137 return; 2138 } 2139 2140 mNumAccounts = accounts.length; 2141 2142 if (accounts.length == 0) { 2143 if (mActivity != null) { 2144 // no accounts, add one now. pretend that the user directly 2145 // made this request 2146 mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures, 2147 mAddAccountOptions, mActivity, mMyCallback, mHandler); 2148 } else { 2149 // send result since we can't prompt to add an account 2150 Bundle result = new Bundle(); 2151 result.putString(KEY_ACCOUNT_NAME, null); 2152 result.putString(KEY_ACCOUNT_TYPE, null); 2153 result.putString(KEY_AUTHTOKEN, null); 2154 try { 2155 mResponse.onResult(result); 2156 } catch (RemoteException e) { 2157 // this will never happen 2158 } 2159 // we are done 2160 } 2161 } else if (accounts.length == 1) { 2162 // have a single account, return an authtoken for it 2163 if (mActivity == null) { 2164 mFuture = getAuthToken(accounts[0], mAuthTokenType, 2165 false /* notifyAuthFailure */, mMyCallback, mHandler); 2166 } else { 2167 mFuture = getAuthToken(accounts[0], 2168 mAuthTokenType, mLoginOptions, 2169 mActivity, mMyCallback, mHandler); 2170 } 2171 } else { 2172 if (mActivity != null) { 2173 IAccountManagerResponse chooseResponse = 2174 new IAccountManagerResponse.Stub() { 2175 public void onResult(Bundle value) throws RemoteException { 2176 Account account = new Account( 2177 value.getString(KEY_ACCOUNT_NAME), 2178 value.getString(KEY_ACCOUNT_TYPE)); 2179 mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, 2180 mActivity, mMyCallback, mHandler); 2181 } 2182 2183 public void onError(int errorCode, String errorMessage) 2184 throws RemoteException { 2185 mResponse.onError(errorCode, errorMessage); 2186 } 2187 }; 2188 // have many accounts, launch the chooser 2189 Intent intent = new Intent(); 2190 ComponentName componentName = ComponentName.unflattenFromString( 2191 Resources.getSystem().getString( 2192 R.string.config_chooseAccountActivity)); 2193 intent.setClassName(componentName.getPackageName(), 2194 componentName.getClassName()); 2195 intent.putExtra(KEY_ACCOUNTS, accounts); 2196 intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE, 2197 new AccountManagerResponse(chooseResponse)); 2198 mActivity.startActivity(intent); 2199 // the result will arrive via the IAccountManagerResponse 2200 } else { 2201 // send result since we can't prompt to select an account 2202 Bundle result = new Bundle(); 2203 result.putString(KEY_ACCOUNTS, null); 2204 try { 2205 mResponse.onResult(result); 2206 } catch (RemoteException e) { 2207 // this will never happen 2208 } 2209 // we are done 2210 } 2211 } 2212 }}, mHandler); 2213 } 2214 2215 public void run(AccountManagerFuture<Bundle> future) { 2216 try { 2217 final Bundle result = future.getResult(); 2218 if (mNumAccounts == 0) { 2219 final String accountName = result.getString(KEY_ACCOUNT_NAME); 2220 final String accountType = result.getString(KEY_ACCOUNT_TYPE); 2221 if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 2222 setException(new AuthenticatorException("account not in result")); 2223 return; 2224 } 2225 final Account account = new Account(accountName, accountType); 2226 mNumAccounts = 1; 2227 getAuthToken(account, mAuthTokenType, null /* options */, mActivity, 2228 mMyCallback, mHandler); 2229 return; 2230 } 2231 set(result); 2232 } catch (OperationCanceledException e) { 2233 cancel(true /* mayInterruptIfRUnning */); 2234 } catch (IOException e) { 2235 setException(e); 2236 } catch (AuthenticatorException e) { 2237 setException(e); 2238 } 2239 } 2240 } 2241 2242 /** 2243 * This convenience helper combines the functionality of 2244 * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and 2245 * {@link #addAccount}. 2246 * 2247 * <p>This method gets a list of the accounts matching the 2248 * specified type and feature set; if there is exactly one, it is 2249 * used; if there are more than one, the user is prompted to pick one; 2250 * if there are none, the user is prompted to add one. Finally, 2251 * an auth token is acquired for the chosen account. 2252 * 2253 * <p>This method may be called from any thread, but the returned 2254 * {@link AccountManagerFuture} must not be used on the main thread. 2255 * 2256 * <p>This method requires the caller to hold the permission 2257 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. 2258 * 2259 * @param accountType The account type required 2260 * (see {@link #getAccountsByType}), must not be null 2261 * @param authTokenType The desired auth token type 2262 * (see {@link #getAuthToken}), must not be null 2263 * @param features Required features for the account 2264 * (see {@link #getAccountsByTypeAndFeatures}), may be null or empty 2265 * @param activity The {@link Activity} context to use for launching new 2266 * sub-Activities to prompt to add an account, select an account, 2267 * and/or enter a password, as necessary; used only to call 2268 * startActivity(); should not be null 2269 * @param addAccountOptions Authenticator-specific options to use for 2270 * adding new accounts; may be null or empty 2271 * @param getAuthTokenOptions Authenticator-specific options to use for 2272 * getting auth tokens; may be null or empty 2273 * @param callback Callback to invoke when the request completes, 2274 * null for no callback 2275 * @param handler {@link Handler} identifying the callback thread, 2276 * null for the main thread 2277 * @return An {@link AccountManagerFuture} which resolves to a Bundle with 2278 * at least the following fields: 2279 * <ul> 2280 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account 2281 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account 2282 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted 2283 * </ul> 2284 * 2285 * If an error occurred, {@link AccountManagerFuture#getResult()} throws: 2286 * <ul> 2287 * <li> {@link AuthenticatorException} if no authenticator was registered for 2288 * this account type or the authenticator failed to respond 2289 * <li> {@link OperationCanceledException} if the operation was canceled for 2290 * any reason, including the user canceling any operation 2291 * <li> {@link IOException} if the authenticator experienced an I/O problem 2292 * updating settings, usually because of network trouble 2293 * </ul> 2294 */ 2295 @RequiresPermission(MANAGE_ACCOUNTS) 2296 public AccountManagerFuture<Bundle> getAuthTokenByFeatures( 2297 final String accountType, final String authTokenType, final String[] features, 2298 final Activity activity, final Bundle addAccountOptions, 2299 final Bundle getAuthTokenOptions, 2300 final AccountManagerCallback<Bundle> callback, final Handler handler) { 2301 if (accountType == null) throw new IllegalArgumentException("account type is null"); 2302 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 2303 final GetAuthTokenByTypeAndFeaturesTask task = 2304 new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features, 2305 activity, addAccountOptions, getAuthTokenOptions, callback, handler); 2306 task.start(); 2307 return task; 2308 } 2309 2310 /** 2311 * Returns an intent to an {@link Activity} that prompts the user to choose from a list of 2312 * accounts. 2313 * The caller will then typically start the activity by calling 2314 * <code>startActivityForResult(intent, ...);</code>. 2315 * <p> 2316 * On success the activity returns a Bundle with the account name and type specified using 2317 * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}. 2318 * <p> 2319 * The most common case is to call this with one account type, e.g.: 2320 * <p> 2321 * <pre> newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null, 2322 * null, null, null);</pre> 2323 * @param selectedAccount if specified, indicates that the {@link Account} is the currently 2324 * selected one, according to the caller's definition of selected. 2325 * @param allowableAccounts an optional {@link ArrayList} of accounts that are allowed to be 2326 * shown. If not specified then this field will not limit the displayed accounts. 2327 * @param allowableAccountTypes an optional string array of account types. These are used 2328 * both to filter the shown accounts and to filter the list of account types that are shown 2329 * when adding an account. If not specified then this field will not limit the displayed 2330 * account types when adding an account. 2331 * @param alwaysPromptForAccount if set the account chooser screen is always shown, otherwise 2332 * it is only shown when there is more than one account from which to choose 2333 * @param descriptionOverrideText if non-null this string is used as the description in the 2334 * accounts chooser screen rather than the default 2335 * @param addAccountAuthTokenType this string is passed as the {@link #addAccount} 2336 * authTokenType parameter 2337 * @param addAccountRequiredFeatures this string array is passed as the {@link #addAccount} 2338 * requiredFeatures parameter 2339 * @param addAccountOptions This {@link Bundle} is passed as the {@link #addAccount} options 2340 * parameter 2341 * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow. 2342 */ 2343 static public Intent newChooseAccountIntent(Account selectedAccount, 2344 ArrayList<Account> allowableAccounts, 2345 String[] allowableAccountTypes, 2346 boolean alwaysPromptForAccount, 2347 String descriptionOverrideText, 2348 String addAccountAuthTokenType, 2349 String[] addAccountRequiredFeatures, 2350 Bundle addAccountOptions) { 2351 Intent intent = new Intent(); 2352 ComponentName componentName = ComponentName.unflattenFromString( 2353 Resources.getSystem().getString(R.string.config_chooseTypeAndAccountActivity)); 2354 intent.setClassName(componentName.getPackageName(), 2355 componentName.getClassName()); 2356 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST, 2357 allowableAccounts); 2358 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY, 2359 allowableAccountTypes); 2360 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE, 2361 addAccountOptions); 2362 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount); 2363 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, 2364 alwaysPromptForAccount); 2365 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_DESCRIPTION_TEXT_OVERRIDE, 2366 descriptionOverrideText); 2367 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING, 2368 addAccountAuthTokenType); 2369 intent.putExtra( 2370 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY, 2371 addAccountRequiredFeatures); 2372 return intent; 2373 } 2374 2375 private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners = 2376 Maps.newHashMap(); 2377 2378 /** 2379 * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent 2380 * so that it can read the updated list of accounts and send them to the listener 2381 * in mAccountsUpdatedListeners. 2382 */ 2383 private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() { 2384 public void onReceive(final Context context, final Intent intent) { 2385 final Account[] accounts = getAccounts(); 2386 // send the result to the listeners 2387 synchronized (mAccountsUpdatedListeners) { 2388 for (Map.Entry<OnAccountsUpdateListener, Handler> entry : 2389 mAccountsUpdatedListeners.entrySet()) { 2390 postToHandler(entry.getValue(), entry.getKey(), accounts); 2391 } 2392 } 2393 } 2394 }; 2395 2396 /** 2397 * Adds an {@link OnAccountsUpdateListener} to this instance of the 2398 * {@link AccountManager}. This listener will be notified whenever the 2399 * list of accounts on the device changes. 2400 * 2401 * <p>As long as this listener is present, the AccountManager instance 2402 * will not be garbage-collected, and neither will the {@link Context} 2403 * used to retrieve it, which may be a large Activity instance. To avoid 2404 * memory leaks, you must remove this listener before then. Normally 2405 * listeners are added in an Activity or Service's {@link Activity#onCreate} 2406 * and removed in {@link Activity#onDestroy}. 2407 * 2408 * <p>It is safe to call this method from the main thread. 2409 * 2410 * <p>This method requires the caller to hold the permission 2411 * {@link android.Manifest.permission#GET_ACCOUNTS}. 2412 * 2413 * @param listener The listener to send notifications to 2414 * @param handler {@link Handler} identifying the thread to use 2415 * for notifications, null for the main thread 2416 * @param updateImmediately If true, the listener will be invoked 2417 * (on the handler thread) right away with the current account list 2418 * @throws IllegalArgumentException if listener is null 2419 * @throws IllegalStateException if listener was already added 2420 */ 2421 @RequiresPermission(GET_ACCOUNTS) 2422 public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener, 2423 Handler handler, boolean updateImmediately) { 2424 if (listener == null) { 2425 throw new IllegalArgumentException("the listener is null"); 2426 } 2427 synchronized (mAccountsUpdatedListeners) { 2428 if (mAccountsUpdatedListeners.containsKey(listener)) { 2429 throw new IllegalStateException("this listener is already added"); 2430 } 2431 final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty(); 2432 2433 mAccountsUpdatedListeners.put(listener, handler); 2434 2435 if (wasEmpty) { 2436 // Register a broadcast receiver to monitor account changes 2437 IntentFilter intentFilter = new IntentFilter(); 2438 intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION); 2439 // To recover from disk-full. 2440 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 2441 mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter); 2442 } 2443 } 2444 2445 if (updateImmediately) { 2446 postToHandler(handler, listener, getAccounts()); 2447 } 2448 } 2449 2450 /** 2451 * Removes an {@link OnAccountsUpdateListener} previously registered with 2452 * {@link #addOnAccountsUpdatedListener}. The listener will no longer 2453 * receive notifications of account changes. 2454 * 2455 * <p>It is safe to call this method from the main thread. 2456 * 2457 * <p>No permission is required to call this method. 2458 * 2459 * @param listener The previously added listener to remove 2460 * @throws IllegalArgumentException if listener is null 2461 * @throws IllegalStateException if listener was not already added 2462 */ 2463 public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) { 2464 if (listener == null) throw new IllegalArgumentException("listener is null"); 2465 synchronized (mAccountsUpdatedListeners) { 2466 if (!mAccountsUpdatedListeners.containsKey(listener)) { 2467 Log.e(TAG, "Listener was not previously added"); 2468 return; 2469 } 2470 mAccountsUpdatedListeners.remove(listener); 2471 if (mAccountsUpdatedListeners.isEmpty()) { 2472 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver); 2473 } 2474 } 2475 } 2476} 2477