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