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