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