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