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