Home | History | Annotate | Download | only in accounts
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.accounts;
     18 
     19 import android.app.Activity;
     20 import android.content.Intent;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.IntentFilter;
     24 import android.content.BroadcastReceiver;
     25 import android.content.res.Resources;
     26 import android.database.SQLException;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.Looper;
     30 import android.os.RemoteException;
     31 import android.os.Parcelable;
     32 import android.os.Build;
     33 import android.os.Process;
     34 import android.os.UserHandle;
     35 import android.util.Log;
     36 import android.text.TextUtils;
     37 
     38 import java.io.IOException;
     39 import java.util.ArrayList;
     40 import java.util.concurrent.Callable;
     41 import java.util.concurrent.CancellationException;
     42 import java.util.concurrent.ExecutionException;
     43 import java.util.concurrent.FutureTask;
     44 import java.util.concurrent.TimeoutException;
     45 import java.util.concurrent.TimeUnit;
     46 import java.util.HashMap;
     47 import java.util.Map;
     48 
     49 import com.android.internal.R;
     50 import com.google.android.collect.Maps;
     51 
     52 /**
     53  * This class provides access to a centralized registry of the user's
     54  * online accounts.  The user enters credentials (username and password) once
     55  * per account, granting applications access to online resources with
     56  * "one-click" approval.
     57  *
     58  * <p>Different online services have different ways of handling accounts and
     59  * authentication, so the account manager uses pluggable <em>authenticator</em>
     60  * modules for different <em>account types</em>.  Authenticators (which may be
     61  * written by third parties) handle the actual details of validating account
     62  * credentials and storing account information.  For example, Google, Facebook,
     63  * and Microsoft Exchange each have their own authenticator.
     64  *
     65  * <p>Many servers support some notion of an <em>authentication token</em>,
     66  * which can be used to authenticate a request to the server without sending
     67  * the user's actual password.  (Auth tokens are normally created with a
     68  * separate request which does include the user's credentials.)  AccountManager
     69  * can generate auth tokens for applications, so the application doesn't need to
     70  * handle passwords directly.  Auth tokens are normally reusable and cached by
     71  * AccountManager, but must be refreshed periodically.  It's the responsibility
     72  * of applications to <em>invalidate</em> auth tokens when they stop working so
     73  * the AccountManager knows it needs to regenerate them.
     74  *
     75  * <p>Applications accessing a server normally go through these steps:
     76  *
     77  * <ul>
     78  * <li>Get an instance of AccountManager using {@link #get(Context)}.
     79  *
     80  * <li>List the available accounts using {@link #getAccountsByType} or
     81  * {@link #getAccountsByTypeAndFeatures}.  Normally applications will only
     82  * be interested in accounts with one particular <em>type</em>, which
     83  * identifies the authenticator.  Account <em>features</em> are used to
     84  * identify particular account subtypes and capabilities.  Both the account
     85  * type and features are authenticator-specific strings, and must be known by
     86  * the application in coordination with its preferred authenticators.
     87  *
     88  * <li>Select one or more of the available accounts, possibly by asking the
     89  * user for their preference.  If no suitable accounts are available,
     90  * {@link #addAccount} may be called to prompt the user to create an
     91  * account of the appropriate type.
     92  *
     93  * <li><b>Important:</b> If the application is using a previously remembered
     94  * account selection, it must make sure the account is still in the list
     95  * of accounts returned by {@link #getAccountsByType}.  Requesting an auth token
     96  * for an account no longer on the device results in an undefined failure.
     97  *
     98  * <li>Request an auth token for the selected account(s) using one of the
     99  * {@link #getAuthToken} methods or related helpers.  Refer to the description
    100  * of each method for exact usage and error handling details.
    101  *
    102  * <li>Make the request using the auth token.  The form of the auth token,
    103  * the format of the request, and the protocol used are all specific to the
    104  * service you are accessing.  The application may use whatever network and
    105  * protocol libraries are useful.
    106  *
    107  * <li><b>Important:</b> If the request fails with an authentication error,
    108  * it could be that a cached auth token is stale and no longer honored by
    109  * the server.  The application must call {@link #invalidateAuthToken} to remove
    110  * the token from the cache, otherwise requests will continue failing!  After
    111  * invalidating the auth token, immediately go back to the "Request an auth
    112  * token" step above.  If the process fails the second time, then it can be
    113  * treated as a "genuine" authentication failure and the user notified or other
    114  * appropriate actions taken.
    115  * </ul>
    116  *
    117  * <p>Some AccountManager methods may need to interact with the user to
    118  * prompt for credentials, present options, or ask the user to add an account.
    119  * The caller may choose whether to allow AccountManager to directly launch the
    120  * necessary user interface and wait for the user, or to return an Intent which
    121  * the caller may use to launch the interface, or (in some cases) to install a
    122  * notification which the user can select at any time to launch the interface.
    123  * To have AccountManager launch the interface directly, the caller must supply
    124  * the current foreground {@link Activity} context.
    125  *
    126  * <p>Many AccountManager methods take {@link AccountManagerCallback} and
    127  * {@link Handler} as parameters.  These methods return immediately and
    128  * run asynchronously. If a callback is provided then
    129  * {@link AccountManagerCallback#run} will be invoked on the Handler's
    130  * thread when the request completes, successfully or not.
    131  * The result is retrieved by calling {@link AccountManagerFuture#getResult()}
    132  * on the {@link AccountManagerFuture} returned by the method (and also passed
    133  * to the callback).  This method waits for the operation to complete (if
    134  * necessary) and either returns the result or throws an exception if an error
    135  * occurred during the operation.  To make the request synchronously, call
    136  * {@link AccountManagerFuture#getResult()} immediately on receiving the
    137  * future from the method; no callback need be supplied.
    138  *
    139  * <p>Requests which may block, including
    140  * {@link AccountManagerFuture#getResult()}, must never be called on
    141  * the application's main event thread.  These operations throw
    142  * {@link IllegalStateException} if they are used on the main thread.
    143  */
    144 public class AccountManager {
    145     private static final String TAG = "AccountManager";
    146 
    147     public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
    148     public static final int ERROR_CODE_NETWORK_ERROR = 3;
    149     public static final int ERROR_CODE_CANCELED = 4;
    150     public static final int ERROR_CODE_INVALID_RESPONSE = 5;
    151     public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
    152     public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
    153     public static final int ERROR_CODE_BAD_REQUEST = 8;
    154     public static final int ERROR_CODE_BAD_AUTHENTICATION = 9;
    155 
    156     /** @hide */
    157     public static final int ERROR_CODE_USER_RESTRICTED = 100;
    158     /** @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      */
    751     public AccountManagerFuture<Boolean> removeAccount(final Account account,
    752             AccountManagerCallback<Boolean> callback, Handler handler) {
    753         if (account == null) throw new IllegalArgumentException("account is null");
    754         return new Future2Task<Boolean>(handler, callback) {
    755             public void doWork() throws RemoteException {
    756                 mService.removeAccount(mResponse, account);
    757             }
    758             public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
    759                 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
    760                     throw new AuthenticatorException("no result in response");
    761                 }
    762                 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
    763             }
    764         }.start();
    765     }
    766 
    767     /**
    768      * @see #removeAccount(Account, AccountManagerCallback, Handler)
    769      * @hide
    770      */
    771     public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account,
    772             AccountManagerCallback<Boolean> callback, Handler handler,
    773             final UserHandle userHandle) {
    774         if (account == null) throw new IllegalArgumentException("account is null");
    775         if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
    776         return new Future2Task<Boolean>(handler, callback) {
    777             public void doWork() throws RemoteException {
    778                 mService.removeAccountAsUser(mResponse, account, userHandle.getIdentifier());
    779             }
    780             public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
    781                 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
    782                     throw new AuthenticatorException("no result in response");
    783                 }
    784                 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
    785             }
    786         }.start();
    787     }
    788 
    789     /**
    790      * Removes an auth token from the AccountManager's cache.  Does nothing if
    791      * the auth token is not currently in the cache.  Applications must call this
    792      * method when the auth token is found to have expired or otherwise become
    793      * invalid for authenticating requests.  The AccountManager does not validate
    794      * or expire cached auth tokens otherwise.
    795      *
    796      * <p>It is safe to call this method from the main thread.
    797      *
    798      * <p>This method requires the caller to hold the permission
    799      * {@link android.Manifest.permission#MANAGE_ACCOUNTS} or
    800      * {@link android.Manifest.permission#USE_CREDENTIALS}
    801      *
    802      * @param accountType The account type of the auth token to invalidate, must not be null
    803      * @param authToken The auth token to invalidate, may be null
    804      */
    805     public void invalidateAuthToken(final String accountType, final String authToken) {
    806         if (accountType == null) throw new IllegalArgumentException("accountType is null");
    807         try {
    808             if (authToken != null) {
    809                 mService.invalidateAuthToken(accountType, authToken);
    810             }
    811         } catch (RemoteException e) {
    812             // won't ever happen
    813             throw new RuntimeException(e);
    814         }
    815     }
    816 
    817     /**
    818      * Gets an auth token from the AccountManager's cache.  If no auth
    819      * token is cached for this account, null will be returned -- a new
    820      * auth token will not be generated, and the server will not be contacted.
    821      * Intended for use by the authenticator, not directly by applications.
    822      *
    823      * <p>It is safe to call this method from the main thread.
    824      *
    825      * <p>This method requires the caller to hold the permission
    826      * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
    827      * and to have the same UID as the account's authenticator.
    828      *
    829      * @param account The account to fetch an auth token for
    830      * @param authTokenType The type of auth token to fetch, see {#getAuthToken}
    831      * @return The cached auth token for this account and type, or null if
    832      *     no auth token is cached or the account does not exist.
    833      */
    834     public String peekAuthToken(final Account account, final String authTokenType) {
    835         if (account == null) throw new IllegalArgumentException("account is null");
    836         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
    837         try {
    838             return mService.peekAuthToken(account, authTokenType);
    839         } catch (RemoteException e) {
    840             // won't ever happen
    841             throw new RuntimeException(e);
    842         }
    843     }
    844 
    845     /**
    846      * Sets or forgets a saved password.  This modifies the local copy of the
    847      * password used to automatically authenticate the user; it does
    848      * not change the user's account password on the server.  Intended for use
    849      * by the authenticator, not directly by applications.
    850      *
    851      * <p>It is safe to call this method from the main thread.
    852      *
    853      * <p>This method requires the caller to hold the permission
    854      * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
    855      * and have the same UID as the account's authenticator.
    856      *
    857      * @param account The account to set a password for
    858      * @param password The password to set, null to clear the password
    859      */
    860     public void setPassword(final Account account, final String password) {
    861         if (account == null) throw new IllegalArgumentException("account is null");
    862         try {
    863             mService.setPassword(account, password);
    864         } catch (RemoteException e) {
    865             // won't ever happen
    866             throw new RuntimeException(e);
    867         }
    868     }
    869 
    870     /**
    871      * Forgets a saved password.  This erases the local copy of the password;
    872      * it does not change the user's account password on the server.
    873      * Has the same effect as setPassword(account, null) but requires fewer
    874      * permissions, and may be used by applications or management interfaces
    875      * to "sign out" from an account.
    876      *
    877      * <p>It is safe to call this method from the main thread.
    878      *
    879      * <p>This method requires the caller to hold the permission
    880      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}
    881      *
    882      * @param account The account whose password to clear
    883      */
    884     public void clearPassword(final Account account) {
    885         if (account == null) throw new IllegalArgumentException("account is null");
    886         try {
    887             mService.clearPassword(account);
    888         } catch (RemoteException e) {
    889             // won't ever happen
    890             throw new RuntimeException(e);
    891         }
    892     }
    893 
    894     /**
    895      * Sets one userdata key for an account.  Intended by use for the
    896      * authenticator to stash state for itself, not directly by applications.
    897      * The meaning of the keys and values is up to the authenticator.
    898      *
    899      * <p>It is safe to call this method from the main thread.
    900      *
    901      * <p>This method requires the caller to hold the permission
    902      * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
    903      * and to have the same UID as the account's authenticator.
    904      *
    905      * @param account The account to set the userdata for
    906      * @param key The userdata key to set.  Must not be null
    907      * @param value The value to set, null to clear this userdata key
    908      */
    909     public void setUserData(final Account account, final String key, final String value) {
    910         if (account == null) throw new IllegalArgumentException("account is null");
    911         if (key == null) throw new IllegalArgumentException("key is null");
    912         try {
    913             mService.setUserData(account, key, value);
    914         } catch (RemoteException e) {
    915             // won't ever happen
    916             throw new RuntimeException(e);
    917         }
    918     }
    919 
    920     /**
    921      * Adds an auth token to the AccountManager cache for an account.
    922      * If the account does not exist then this call has no effect.
    923      * Replaces any previous auth token for this account and auth token type.
    924      * Intended for use by the authenticator, not directly by applications.
    925      *
    926      * <p>It is safe to call this method from the main thread.
    927      *
    928      * <p>This method requires the caller to hold the permission
    929      * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
    930      * and to have the same UID as the account's authenticator.
    931      *
    932      * @param account The account to set an auth token for
    933      * @param authTokenType The type of the auth token, see {#getAuthToken}
    934      * @param authToken The auth token to add to the cache
    935      */
    936     public void setAuthToken(Account account, final String authTokenType, final String authToken) {
    937         if (account == null) throw new IllegalArgumentException("account is null");
    938         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
    939         try {
    940             mService.setAuthToken(account, authTokenType, authToken);
    941         } catch (RemoteException e) {
    942             // won't ever happen
    943             throw new RuntimeException(e);
    944         }
    945     }
    946 
    947     /**
    948      * This convenience helper synchronously gets an auth token with
    949      * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}.
    950      *
    951      * <p>This method may block while a network request completes, and must
    952      * never be made from the main thread.
    953      *
    954      * <p>This method requires the caller to hold the permission
    955      * {@link android.Manifest.permission#USE_CREDENTIALS}.
    956      *
    957      * @param account The account to fetch an auth token for
    958      * @param authTokenType The auth token type, see {@link #getAuthToken getAuthToken()}
    959      * @param notifyAuthFailure If true, display a notification and return null
    960      *     if authentication fails; if false, prompt and wait for the user to
    961      *     re-enter correct credentials before returning
    962      * @return An auth token of the specified type for this account, or null
    963      *     if authentication fails or none can be fetched.
    964      * @throws AuthenticatorException if the authenticator failed to respond
    965      * @throws OperationCanceledException if the request was canceled for any
    966      *     reason, including the user canceling a credential request
    967      * @throws java.io.IOException if the authenticator experienced an I/O problem
    968      *     creating a new auth token, usually because of network trouble
    969      */
    970     public String blockingGetAuthToken(Account account, String authTokenType,
    971             boolean notifyAuthFailure)
    972             throws OperationCanceledException, IOException, AuthenticatorException {
    973         if (account == null) throw new IllegalArgumentException("account is null");
    974         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
    975         Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
    976                 null /* handler */).getResult();
    977         if (bundle == null) {
    978             // This should never happen, but it does, occasionally. If it does return null to
    979             // signify that we were not able to get the authtoken.
    980             // TODO: remove this when the bug is found that sometimes causes a null bundle to be
    981             // returned
    982             Log.e(TAG, "blockingGetAuthToken: null was returned from getResult() for "
    983                     + account + ", authTokenType " + authTokenType);
    984             return null;
    985         }
    986         return bundle.getString(KEY_AUTHTOKEN);
    987     }
    988 
    989     /**
    990      * Gets an auth token of the specified type for a particular account,
    991      * prompting the user for credentials if necessary.  This method is
    992      * intended for applications running in the foreground where it makes
    993      * sense to ask the user directly for a password.
    994      *
    995      * <p>If a previously generated auth token is cached for this account and
    996      * type, then it is returned.  Otherwise, if a saved password is
    997      * available, it is sent to the server to generate a new auth token.
    998      * Otherwise, the user is prompted to enter a password.
    999      *
   1000      * <p>Some authenticators have auth token <em>types</em>, whose value
   1001      * is authenticator-dependent.  Some services use different token types to
   1002      * access different functionality -- for example, Google uses different auth
   1003      * tokens to access Gmail and Google Calendar for the same account.
   1004      *
   1005      * <p>This method may be called from any thread, but the returned
   1006      * {@link AccountManagerFuture} must not be used on the main thread.
   1007      *
   1008      * <p>This method requires the caller to hold the permission
   1009      * {@link android.Manifest.permission#USE_CREDENTIALS}.
   1010      *
   1011      * @param account The account to fetch an auth token for
   1012      * @param authTokenType The auth token type, an authenticator-dependent
   1013      *     string token, must not be null
   1014      * @param options Authenticator-specific options for the request,
   1015      *     may be null or empty
   1016      * @param activity The {@link Activity} context to use for launching a new
   1017      *     authenticator-defined sub-Activity to prompt the user for a password
   1018      *     if necessary; used only to call startActivity(); must not be null.
   1019      * @param callback Callback to invoke when the request completes,
   1020      *     null for no callback
   1021      * @param handler {@link Handler} identifying the callback thread,
   1022      *     null for the main thread
   1023      * @return An {@link AccountManagerFuture} which resolves to a Bundle with
   1024      *     at least the following fields:
   1025      * <ul>
   1026      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
   1027      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1028      * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
   1029      * </ul>
   1030      *
   1031      * (Other authenticator-specific values may be returned.)  If an auth token
   1032      * could not be fetched, {@link AccountManagerFuture#getResult()} throws:
   1033      * <ul>
   1034      * <li> {@link AuthenticatorException} if the authenticator failed to respond
   1035      * <li> {@link OperationCanceledException} if the operation is canceled for
   1036      *      any reason, incluidng the user canceling a credential request
   1037      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1038      *      creating a new auth token, usually because of network trouble
   1039      * </ul>
   1040      * If the account is no longer present on the device, the return value is
   1041      * authenticator-dependent.  The caller should verify the validity of the
   1042      * account before requesting an auth token.
   1043      */
   1044     public AccountManagerFuture<Bundle> getAuthToken(
   1045             final Account account, final String authTokenType, final Bundle options,
   1046             final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
   1047         if (account == null) throw new IllegalArgumentException("account is null");
   1048         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
   1049         final Bundle optionsIn = new Bundle();
   1050         if (options != null) {
   1051             optionsIn.putAll(options);
   1052         }
   1053         optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
   1054         return new AmsTask(activity, handler, callback) {
   1055             public void doWork() throws RemoteException {
   1056                 mService.getAuthToken(mResponse, account, authTokenType,
   1057                         false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
   1058                         optionsIn);
   1059             }
   1060         }.start();
   1061     }
   1062 
   1063     /**
   1064      * Gets an auth token of the specified type for a particular account,
   1065      * optionally raising a notification if the user must enter credentials.
   1066      * This method is intended for background tasks and services where the
   1067      * user should not be immediately interrupted with a password prompt.
   1068      *
   1069      * <p>If a previously generated auth token is cached for this account and
   1070      * type, then it is returned.  Otherwise, if a saved password is
   1071      * available, it is sent to the server to generate a new auth token.
   1072      * Otherwise, an {@link Intent} is returned which, when started, will
   1073      * prompt the user for a password.  If the notifyAuthFailure parameter is
   1074      * set, a status bar notification is also created with the same Intent,
   1075      * alerting the user that they need to enter a password at some point.
   1076      *
   1077      * <p>In that case, you may need to wait until the user responds, which
   1078      * could take hours or days or forever.  When the user does respond and
   1079      * supply a new password, the account manager will broadcast the
   1080      * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
   1081      * use to try again.
   1082      *
   1083      * <p>If notifyAuthFailure is not set, it is the application's
   1084      * responsibility to launch the returned Intent at some point.
   1085      * Either way, the result from this call will not wait for user action.
   1086      *
   1087      * <p>Some authenticators have auth token <em>types</em>, whose value
   1088      * is authenticator-dependent.  Some services use different token types to
   1089      * access different functionality -- for example, Google uses different auth
   1090      * tokens to access Gmail and Google Calendar for the same account.
   1091      *
   1092      * <p>This method may be called from any thread, but the returned
   1093      * {@link AccountManagerFuture} must not be used on the main thread.
   1094      *
   1095      * <p>This method requires the caller to hold the permission
   1096      * {@link android.Manifest.permission#USE_CREDENTIALS}.
   1097      *
   1098      * @param account The account to fetch an auth token for
   1099      * @param authTokenType The auth token type, an authenticator-dependent
   1100      *     string token, must not be null
   1101      * @param notifyAuthFailure True to add a notification to prompt the
   1102      *     user for a password if necessary, false to leave that to the caller
   1103      * @param callback Callback to invoke when the request completes,
   1104      *     null for no callback
   1105      * @param handler {@link Handler} identifying the callback thread,
   1106      *     null for the main thread
   1107      * @return An {@link AccountManagerFuture} which resolves to a Bundle with
   1108      *     at least the following fields on success:
   1109      * <ul>
   1110      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
   1111      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1112      * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
   1113      * </ul>
   1114      *
   1115      * (Other authenticator-specific values may be returned.)  If the user
   1116      * must enter credentials, the returned Bundle contains only
   1117      * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
   1118      *
   1119      * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
   1120      * <ul>
   1121      * <li> {@link AuthenticatorException} if the authenticator failed to respond
   1122      * <li> {@link OperationCanceledException} if the operation is canceled for
   1123      *      any reason, incluidng the user canceling a credential request
   1124      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1125      *      creating a new auth token, usually because of network trouble
   1126      * </ul>
   1127      * If the account is no longer present on the device, the return value is
   1128      * authenticator-dependent.  The caller should verify the validity of the
   1129      * account before requesting an auth token.
   1130      * @deprecated use {@link #getAuthToken(Account, String, android.os.Bundle,
   1131      * boolean, AccountManagerCallback, android.os.Handler)} instead
   1132      */
   1133     @Deprecated
   1134     public AccountManagerFuture<Bundle> getAuthToken(
   1135             final Account account, final String authTokenType,
   1136             final boolean notifyAuthFailure,
   1137             AccountManagerCallback<Bundle> callback, Handler handler) {
   1138         return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback,
   1139                 handler);
   1140     }
   1141 
   1142     /**
   1143      * Gets an auth token of the specified type for a particular account,
   1144      * optionally raising a notification if the user must enter credentials.
   1145      * This method is intended for background tasks and services where the
   1146      * user should not be immediately interrupted with a password prompt.
   1147      *
   1148      * <p>If a previously generated auth token is cached for this account and
   1149      * type, then it is returned.  Otherwise, if a saved password is
   1150      * available, it is sent to the server to generate a new auth token.
   1151      * Otherwise, an {@link Intent} is returned which, when started, will
   1152      * prompt the user for a password.  If the notifyAuthFailure parameter is
   1153      * set, a status bar notification is also created with the same Intent,
   1154      * alerting the user that they need to enter a password at some point.
   1155      *
   1156      * <p>In that case, you may need to wait until the user responds, which
   1157      * could take hours or days or forever.  When the user does respond and
   1158      * supply a new password, the account manager will broadcast the
   1159      * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
   1160      * use to try again.
   1161      *
   1162      * <p>If notifyAuthFailure is not set, it is the application's
   1163      * responsibility to launch the returned Intent at some point.
   1164      * Either way, the result from this call will not wait for user action.
   1165      *
   1166      * <p>Some authenticators have auth token <em>types</em>, whose value
   1167      * is authenticator-dependent.  Some services use different token types to
   1168      * access different functionality -- for example, Google uses different auth
   1169      * tokens to access Gmail and Google Calendar for the same account.
   1170      *
   1171      * <p>This method may be called from any thread, but the returned
   1172      * {@link AccountManagerFuture} must not be used on the main thread.
   1173      *
   1174      * <p>This method requires the caller to hold the permission
   1175      * {@link android.Manifest.permission#USE_CREDENTIALS}.
   1176      *
   1177      * @param account The account to fetch an auth token for
   1178      * @param authTokenType The auth token type, an authenticator-dependent
   1179      *     string token, must not be null
   1180      * @param options Authenticator-specific options for the request,
   1181      *     may be null or empty
   1182      * @param notifyAuthFailure True to add a notification to prompt the
   1183      *     user for a password if necessary, false to leave that to the caller
   1184      * @param callback Callback to invoke when the request completes,
   1185      *     null for no callback
   1186      * @param handler {@link Handler} identifying the callback thread,
   1187      *     null for the main thread
   1188      * @return An {@link AccountManagerFuture} which resolves to a Bundle with
   1189      *     at least the following fields on success:
   1190      * <ul>
   1191      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
   1192      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1193      * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
   1194      * </ul>
   1195      *
   1196      * (Other authenticator-specific values may be returned.)  If the user
   1197      * must enter credentials, the returned Bundle contains only
   1198      * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
   1199      *
   1200      * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
   1201      * <ul>
   1202      * <li> {@link AuthenticatorException} if the authenticator failed to respond
   1203      * <li> {@link OperationCanceledException} if the operation is canceled for
   1204      *      any reason, incluidng the user canceling a credential request
   1205      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1206      *      creating a new auth token, usually because of network trouble
   1207      * </ul>
   1208      * If the account is no longer present on the device, the return value is
   1209      * authenticator-dependent.  The caller should verify the validity of the
   1210      * account before requesting an auth token.
   1211      */
   1212     public AccountManagerFuture<Bundle> getAuthToken(
   1213             final Account account, final String authTokenType, final Bundle options,
   1214             final boolean notifyAuthFailure,
   1215             AccountManagerCallback<Bundle> callback, Handler handler) {
   1216 
   1217         if (account == null) throw new IllegalArgumentException("account is null");
   1218         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
   1219         final Bundle optionsIn = new Bundle();
   1220         if (options != null) {
   1221             optionsIn.putAll(options);
   1222         }
   1223         optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
   1224         return new AmsTask(null, handler, callback) {
   1225             public void doWork() throws RemoteException {
   1226                 mService.getAuthToken(mResponse, account, authTokenType,
   1227                         notifyAuthFailure, false /* expectActivityLaunch */, optionsIn);
   1228             }
   1229         }.start();
   1230     }
   1231 
   1232     /**
   1233      * Asks the user to add an account of a specified type.  The authenticator
   1234      * for this account type processes this request with the appropriate user
   1235      * interface.  If the user does elect to create a new account, the account
   1236      * name is returned.
   1237      *
   1238      * <p>This method may be called from any thread, but the returned
   1239      * {@link AccountManagerFuture} must not be used on the main thread.
   1240      *
   1241      * <p>This method requires the caller to hold the permission
   1242      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
   1243      *
   1244      * @param accountType The type of account to add; must not be null
   1245      * @param authTokenType The type of auth token (see {@link #getAuthToken})
   1246      *     this account will need to be able to generate, null for none
   1247      * @param requiredFeatures The features (see {@link #hasFeatures}) this
   1248      *     account must have, null for none
   1249      * @param addAccountOptions Authenticator-specific options for the request,
   1250      *     may be null or empty
   1251      * @param activity The {@link Activity} context to use for launching a new
   1252      *     authenticator-defined sub-Activity to prompt the user to create an
   1253      *     account; used only to call startActivity(); if null, the prompt
   1254      *     will not be launched directly, but the necessary {@link Intent}
   1255      *     will be returned to the caller instead
   1256      * @param callback Callback to invoke when the request completes,
   1257      *     null for no callback
   1258      * @param handler {@link Handler} identifying the callback thread,
   1259      *     null for the main thread
   1260      * @return An {@link AccountManagerFuture} which resolves to a Bundle with
   1261      *     these fields if activity was specified and an account was created:
   1262      * <ul>
   1263      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
   1264      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1265      * </ul>
   1266      *
   1267      * If no activity was specified, the returned Bundle contains only
   1268      * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
   1269      * actual account creation process.  If an error occurred,
   1270      * {@link AccountManagerFuture#getResult()} throws:
   1271      * <ul>
   1272      * <li> {@link AuthenticatorException} if no authenticator was registered for
   1273      *      this account type or the authenticator failed to respond
   1274      * <li> {@link OperationCanceledException} if the operation was canceled for
   1275      *      any reason, including the user canceling the creation process or adding accounts
   1276      *      (of this type) has been disabled by policy
   1277      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1278      *      creating a new account, usually because of network trouble
   1279      * </ul>
   1280      */
   1281     public AccountManagerFuture<Bundle> addAccount(final String accountType,
   1282             final String authTokenType, final String[] requiredFeatures,
   1283             final Bundle addAccountOptions,
   1284             final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
   1285         if (accountType == null) throw new IllegalArgumentException("accountType is null");
   1286         final Bundle optionsIn = new Bundle();
   1287         if (addAccountOptions != null) {
   1288             optionsIn.putAll(addAccountOptions);
   1289         }
   1290         optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
   1291 
   1292         return new AmsTask(activity, handler, callback) {
   1293             public void doWork() throws RemoteException {
   1294                 mService.addAccount(mResponse, accountType, authTokenType,
   1295                         requiredFeatures, activity != null, optionsIn);
   1296             }
   1297         }.start();
   1298     }
   1299 
   1300     /**
   1301      * @see #addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback, Handler)
   1302      * @hide
   1303      */
   1304     public AccountManagerFuture<Bundle> addAccountAsUser(final String accountType,
   1305             final String authTokenType, final String[] requiredFeatures,
   1306             final Bundle addAccountOptions, final Activity activity,
   1307             AccountManagerCallback<Bundle> callback, Handler handler, final UserHandle userHandle) {
   1308         if (accountType == null) throw new IllegalArgumentException("accountType is null");
   1309         if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
   1310         final Bundle optionsIn = new Bundle();
   1311         if (addAccountOptions != null) {
   1312             optionsIn.putAll(addAccountOptions);
   1313         }
   1314         optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
   1315 
   1316         return new AmsTask(activity, handler, callback) {
   1317             public void doWork() throws RemoteException {
   1318                 mService.addAccountAsUser(mResponse, accountType, authTokenType,
   1319                         requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier());
   1320             }
   1321         }.start();
   1322     }
   1323 
   1324     /**
   1325      * Adds a shared account from the primary user to a secondary user. Adding the shared account
   1326      * doesn't take effect immediately. When the target user starts up, any pending shared accounts
   1327      * are attempted to be copied to the target user from the primary via calls to the
   1328      * authenticator.
   1329      * @param account the account to share
   1330      * @param user the target user
   1331      * @return
   1332      * @hide
   1333      */
   1334     public boolean addSharedAccount(final Account account, UserHandle user) {
   1335         try {
   1336             boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
   1337             return val;
   1338         } catch (RemoteException re) {
   1339             // won't ever happen
   1340             throw new RuntimeException(re);
   1341         }
   1342     }
   1343 
   1344     /**
   1345      * @hide
   1346      * Removes the shared account.
   1347      * @param account the account to remove
   1348      * @param user the user to remove the account from
   1349      * @return
   1350      */
   1351     public boolean removeSharedAccount(final Account account, UserHandle user) {
   1352         try {
   1353             boolean val = mService.removeSharedAccountAsUser(account, user.getIdentifier());
   1354             return val;
   1355         } catch (RemoteException re) {
   1356             // won't ever happen
   1357             throw new RuntimeException(re);
   1358         }
   1359     }
   1360 
   1361     /**
   1362      * @hide
   1363      * @param user
   1364      * @return
   1365      */
   1366     public Account[] getSharedAccounts(UserHandle user) {
   1367         try {
   1368             return mService.getSharedAccountsAsUser(user.getIdentifier());
   1369         } catch (RemoteException re) {
   1370             // won't ever happen
   1371             throw new RuntimeException(re);
   1372         }
   1373     }
   1374 
   1375     /**
   1376      * Confirms that the user knows the password for an account to make extra
   1377      * sure they are the owner of the account.  The user-entered password can
   1378      * be supplied directly, otherwise the authenticator for this account type
   1379      * prompts the user with the appropriate interface.  This method is
   1380      * intended for applications which want extra assurance; for example, the
   1381      * phone lock screen uses this to let the user unlock the phone with an
   1382      * account password if they forget the lock pattern.
   1383      *
   1384      * <p>If the user-entered password matches a saved password for this
   1385      * account, the request is considered valid; otherwise the authenticator
   1386      * verifies the password (usually by contacting the server).
   1387      *
   1388      * <p>This method may be called from any thread, but the returned
   1389      * {@link AccountManagerFuture} must not be used on the main thread.
   1390      *
   1391      * <p>This method requires the caller to hold the permission
   1392      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
   1393      *
   1394      * @param account The account to confirm password knowledge for
   1395      * @param options Authenticator-specific options for the request;
   1396      *     if the {@link #KEY_PASSWORD} string field is present, the
   1397      *     authenticator may use it directly rather than prompting the user;
   1398      *     may be null or empty
   1399      * @param activity The {@link Activity} context to use for launching a new
   1400      *     authenticator-defined sub-Activity to prompt the user to enter a
   1401      *     password; used only to call startActivity(); if null, the prompt
   1402      *     will not be launched directly, but the necessary {@link Intent}
   1403      *     will be returned to the caller instead
   1404      * @param callback Callback to invoke when the request completes,
   1405      *     null for no callback
   1406      * @param handler {@link Handler} identifying the callback thread,
   1407      *     null for the main thread
   1408      * @return An {@link AccountManagerFuture} which resolves to a Bundle
   1409      *     with these fields if activity or password was supplied and
   1410      *     the account was successfully verified:
   1411      * <ul>
   1412      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
   1413      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1414      * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
   1415      * </ul>
   1416      *
   1417      * If no activity or password was specified, the returned Bundle contains
   1418      * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
   1419      * password prompt.  If an error occurred,
   1420      * {@link AccountManagerFuture#getResult()} throws:
   1421      * <ul>
   1422      * <li> {@link AuthenticatorException} if the authenticator failed to respond
   1423      * <li> {@link OperationCanceledException} if the operation was canceled for
   1424      *      any reason, including the user canceling the password prompt
   1425      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1426      *      verifying the password, usually because of network trouble
   1427      * </ul>
   1428      */
   1429     public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
   1430             final Bundle options,
   1431             final Activity activity,
   1432             final AccountManagerCallback<Bundle> callback,
   1433             final Handler handler) {
   1434         return confirmCredentialsAsUser(account, options, activity, callback, handler,
   1435                 Process.myUserHandle());
   1436     }
   1437 
   1438     /**
   1439      * @hide
   1440      * Same as {@link #confirmCredentials(Account, Bundle, Activity, AccountManagerCallback, Handler)}
   1441      * but for the specified user.
   1442      */
   1443     public AccountManagerFuture<Bundle> confirmCredentialsAsUser(final Account account,
   1444             final Bundle options,
   1445             final Activity activity,
   1446             final AccountManagerCallback<Bundle> callback,
   1447             final Handler handler, UserHandle userHandle) {
   1448         if (account == null) throw new IllegalArgumentException("account is null");
   1449         final int userId = userHandle.getIdentifier();
   1450         return new AmsTask(activity, handler, callback) {
   1451             public void doWork() throws RemoteException {
   1452                 mService.confirmCredentialsAsUser(mResponse, account, options, activity != null,
   1453                         userId);
   1454             }
   1455         }.start();
   1456     }
   1457 
   1458     /**
   1459      * Asks the user to enter a new password for an account, updating the
   1460      * saved credentials for the account.  Normally this happens automatically
   1461      * when the server rejects credentials during an auth token fetch, but this
   1462      * can be invoked directly to ensure we have the correct credentials stored.
   1463      *
   1464      * <p>This method may be called from any thread, but the returned
   1465      * {@link AccountManagerFuture} must not be used on the main thread.
   1466      *
   1467      * <p>This method requires the caller to hold the permission
   1468      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
   1469      *
   1470      * @param account The account to update credentials for
   1471      * @param authTokenType The credentials entered must allow an auth token
   1472      *     of this type to be created (but no actual auth token is returned);
   1473      *     may be null
   1474      * @param options Authenticator-specific options for the request;
   1475      *     may be null or empty
   1476      * @param activity The {@link Activity} context to use for launching a new
   1477      *     authenticator-defined sub-Activity to prompt the user to enter a
   1478      *     password; used only to call startActivity(); if null, the prompt
   1479      *     will not be launched directly, but the necessary {@link Intent}
   1480      *     will be returned to the caller instead
   1481      * @param callback Callback to invoke when the request completes,
   1482      *     null for no callback
   1483      * @param handler {@link Handler} identifying the callback thread,
   1484      *     null for the main thread
   1485      * @return An {@link AccountManagerFuture} which resolves to a Bundle
   1486      *     with these fields if an activity was supplied and the account
   1487      *     credentials were successfully updated:
   1488      * <ul>
   1489      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
   1490      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   1491      * </ul>
   1492      *
   1493      * If no activity was specified, the returned Bundle contains only
   1494      * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
   1495      * password prompt.  If an error occurred,
   1496      * {@link AccountManagerFuture#getResult()} throws:
   1497      * <ul>
   1498      * <li> {@link AuthenticatorException} if the authenticator failed to respond
   1499      * <li> {@link OperationCanceledException} if the operation was canceled for
   1500      *      any reason, including the user canceling the password prompt
   1501      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1502      *      verifying the password, usually because of network trouble
   1503      * </ul>
   1504      */
   1505     public AccountManagerFuture<Bundle> updateCredentials(final Account account,
   1506             final String authTokenType,
   1507             final Bundle options, final Activity activity,
   1508             final AccountManagerCallback<Bundle> callback,
   1509             final Handler handler) {
   1510         if (account == null) throw new IllegalArgumentException("account is null");
   1511         return new AmsTask(activity, handler, callback) {
   1512             public void doWork() throws RemoteException {
   1513                 mService.updateCredentials(mResponse, account, authTokenType, activity != null,
   1514                         options);
   1515             }
   1516         }.start();
   1517     }
   1518 
   1519     /**
   1520      * Offers the user an opportunity to change an authenticator's settings.
   1521      * These properties are for the authenticator in general, not a particular
   1522      * account.  Not all authenticators support this method.
   1523      *
   1524      * <p>This method may be called from any thread, but the returned
   1525      * {@link AccountManagerFuture} must not be used on the main thread.
   1526      *
   1527      * <p>This method requires the caller to hold the permission
   1528      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
   1529      *
   1530      * @param accountType The account type associated with the authenticator
   1531      *     to adjust
   1532      * @param activity The {@link Activity} context to use for launching a new
   1533      *     authenticator-defined sub-Activity to adjust authenticator settings;
   1534      *     used only to call startActivity(); if null, the settings dialog will
   1535      *     not be launched directly, but the necessary {@link Intent} will be
   1536      *     returned to the caller instead
   1537      * @param callback Callback to invoke when the request completes,
   1538      *     null for no callback
   1539      * @param handler {@link Handler} identifying the callback thread,
   1540      *     null for the main thread
   1541      * @return An {@link AccountManagerFuture} which resolves to a Bundle
   1542      *     which is empty if properties were edited successfully, or
   1543      *     if no activity was specified, contains only {@link #KEY_INTENT}
   1544      *     needed to launch the authenticator's settings dialog.
   1545      *     If an error occurred, {@link AccountManagerFuture#getResult()}
   1546      *     throws:
   1547      * <ul>
   1548      * <li> {@link AuthenticatorException} if no authenticator was registered for
   1549      *      this account type or the authenticator failed to respond
   1550      * <li> {@link OperationCanceledException} if the operation was canceled for
   1551      *      any reason, including the user canceling the settings dialog
   1552      * <li> {@link IOException} if the authenticator experienced an I/O problem
   1553      *      updating settings, usually because of network trouble
   1554      * </ul>
   1555      */
   1556     public AccountManagerFuture<Bundle> editProperties(final String accountType,
   1557             final Activity activity, final AccountManagerCallback<Bundle> callback,
   1558             final Handler handler) {
   1559         if (accountType == null) throw new IllegalArgumentException("accountType is null");
   1560         return new AmsTask(activity, handler, callback) {
   1561             public void doWork() throws RemoteException {
   1562                 mService.editProperties(mResponse, accountType, activity != null);
   1563             }
   1564         }.start();
   1565     }
   1566 
   1567     private void ensureNotOnMainThread() {
   1568         final Looper looper = Looper.myLooper();
   1569         if (looper != null && looper == mContext.getMainLooper()) {
   1570             final IllegalStateException exception = new IllegalStateException(
   1571                     "calling this from your main thread can lead to deadlock");
   1572             Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
   1573                     exception);
   1574             if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) {
   1575                 throw exception;
   1576             }
   1577         }
   1578     }
   1579 
   1580     private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback,
   1581             final AccountManagerFuture<Bundle> future) {
   1582         handler = handler == null ? mMainHandler : handler;
   1583         handler.post(new Runnable() {
   1584             public void run() {
   1585                 callback.run(future);
   1586             }
   1587         });
   1588     }
   1589 
   1590     private void postToHandler(Handler handler, final OnAccountsUpdateListener listener,
   1591             final Account[] accounts) {
   1592         final Account[] accountsCopy = new Account[accounts.length];
   1593         // send a copy to make sure that one doesn't
   1594         // change what another sees
   1595         System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length);
   1596         handler = (handler == null) ? mMainHandler : handler;
   1597         handler.post(new Runnable() {
   1598             public void run() {
   1599                 try {
   1600                     listener.onAccountsUpdated(accountsCopy);
   1601                 } catch (SQLException e) {
   1602                     // Better luck next time.  If the problem was disk-full,
   1603                     // the STORAGE_OK intent will re-trigger the update.
   1604                     Log.e(TAG, "Can't update accounts", e);
   1605                 }
   1606             }
   1607         });
   1608     }
   1609 
   1610     private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
   1611         final IAccountManagerResponse mResponse;
   1612         final Handler mHandler;
   1613         final AccountManagerCallback<Bundle> mCallback;
   1614         final Activity mActivity;
   1615         public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
   1616             super(new Callable<Bundle>() {
   1617                 public Bundle call() throws Exception {
   1618                     throw new IllegalStateException("this should never be called");
   1619                 }
   1620             });
   1621 
   1622             mHandler = handler;
   1623             mCallback = callback;
   1624             mActivity = activity;
   1625             mResponse = new Response();
   1626         }
   1627 
   1628         public final AccountManagerFuture<Bundle> start() {
   1629             try {
   1630                 doWork();
   1631             } catch (RemoteException e) {
   1632                 setException(e);
   1633             }
   1634             return this;
   1635         }
   1636 
   1637         protected void set(Bundle bundle) {
   1638             // TODO: somehow a null is being set as the result of the Future. Log this
   1639             // case to help debug where this is occurring. When this bug is fixed this
   1640             // condition statement should be removed.
   1641             if (bundle == null) {
   1642                 Log.e(TAG, "the bundle must not be null", new Exception());
   1643             }
   1644             super.set(bundle);
   1645         }
   1646 
   1647         public abstract void doWork() throws RemoteException;
   1648 
   1649         private Bundle internalGetResult(Long timeout, TimeUnit unit)
   1650                 throws OperationCanceledException, IOException, AuthenticatorException {
   1651             if (!isDone()) {
   1652                 ensureNotOnMainThread();
   1653             }
   1654             try {
   1655                 if (timeout == null) {
   1656                     return get();
   1657                 } else {
   1658                     return get(timeout, unit);
   1659                 }
   1660             } catch (CancellationException e) {
   1661                 throw new OperationCanceledException();
   1662             } catch (TimeoutException e) {
   1663                 // fall through and cancel
   1664             } catch (InterruptedException e) {
   1665                 // fall through and cancel
   1666             } catch (ExecutionException e) {
   1667                 final Throwable cause = e.getCause();
   1668                 if (cause instanceof IOException) {
   1669                     throw (IOException) cause;
   1670                 } else if (cause instanceof UnsupportedOperationException) {
   1671                     throw new AuthenticatorException(cause);
   1672                 } else if (cause instanceof AuthenticatorException) {
   1673                     throw (AuthenticatorException) cause;
   1674                 } else if (cause instanceof RuntimeException) {
   1675                     throw (RuntimeException) cause;
   1676                 } else if (cause instanceof Error) {
   1677                     throw (Error) cause;
   1678                 } else {
   1679                     throw new IllegalStateException(cause);
   1680                 }
   1681             } finally {
   1682                 cancel(true /* interrupt if running */);
   1683             }
   1684             throw new OperationCanceledException();
   1685         }
   1686 
   1687         public Bundle getResult()
   1688                 throws OperationCanceledException, IOException, AuthenticatorException {
   1689             return internalGetResult(null, null);
   1690         }
   1691 
   1692         public Bundle getResult(long timeout, TimeUnit unit)
   1693                 throws OperationCanceledException, IOException, AuthenticatorException {
   1694             return internalGetResult(timeout, unit);
   1695         }
   1696 
   1697         protected void done() {
   1698             if (mCallback != null) {
   1699                 postToHandler(mHandler, mCallback, this);
   1700             }
   1701         }
   1702 
   1703         /** Handles the responses from the AccountManager */
   1704         private class Response extends IAccountManagerResponse.Stub {
   1705             public void onResult(Bundle bundle) {
   1706                 Intent intent = bundle.getParcelable(KEY_INTENT);
   1707                 if (intent != null && mActivity != null) {
   1708                     // since the user provided an Activity we will silently start intents
   1709                     // that we see
   1710                     mActivity.startActivity(intent);
   1711                     // leave the Future running to wait for the real response to this request
   1712                 } else if (bundle.getBoolean("retry")) {
   1713                     try {
   1714                         doWork();
   1715                     } catch (RemoteException e) {
   1716                         // this will only happen if the system process is dead, which means
   1717                         // we will be dying ourselves
   1718                     }
   1719                 } else {
   1720                     set(bundle);
   1721                 }
   1722             }
   1723 
   1724             public void onError(int code, String message) {
   1725                 if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
   1726                         || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
   1727                     // the authenticator indicated that this request was canceled or we were
   1728                     // forbidden to fulfill; cancel now
   1729                     cancel(true /* mayInterruptIfRunning */);
   1730                     return;
   1731                 }
   1732                 setException(convertErrorToException(code, message));
   1733             }
   1734         }
   1735 
   1736     }
   1737 
   1738     private abstract class BaseFutureTask<T> extends FutureTask<T> {
   1739         final public IAccountManagerResponse mResponse;
   1740         final Handler mHandler;
   1741 
   1742         public BaseFutureTask(Handler handler) {
   1743             super(new Callable<T>() {
   1744                 public T call() throws Exception {
   1745                     throw new IllegalStateException("this should never be called");
   1746                 }
   1747             });
   1748             mHandler = handler;
   1749             mResponse = new Response();
   1750         }
   1751 
   1752         public abstract void doWork() throws RemoteException;
   1753 
   1754         public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException;
   1755 
   1756         protected void postRunnableToHandler(Runnable runnable) {
   1757             Handler handler = (mHandler == null) ? mMainHandler : mHandler;
   1758             handler.post(runnable);
   1759         }
   1760 
   1761         protected void startTask() {
   1762             try {
   1763                 doWork();
   1764             } catch (RemoteException e) {
   1765                 setException(e);
   1766             }
   1767         }
   1768 
   1769         protected class Response extends IAccountManagerResponse.Stub {
   1770             public void onResult(Bundle bundle) {
   1771                 try {
   1772                     T result = bundleToResult(bundle);
   1773                     if (result == null) {
   1774                         return;
   1775                     }
   1776                     set(result);
   1777                     return;
   1778                 } catch (ClassCastException e) {
   1779                     // we will set the exception below
   1780                 } catch (AuthenticatorException e) {
   1781                     // we will set the exception below
   1782                 }
   1783                 onError(ERROR_CODE_INVALID_RESPONSE, "no result in response");
   1784             }
   1785 
   1786             public void onError(int code, String message) {
   1787                 if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
   1788                         || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
   1789                     // the authenticator indicated that this request was canceled or we were
   1790                     // forbidden to fulfill; cancel now
   1791                     cancel(true /* mayInterruptIfRunning */);
   1792                     return;
   1793                 }
   1794                 setException(convertErrorToException(code, message));
   1795             }
   1796         }
   1797     }
   1798 
   1799     private abstract class Future2Task<T>
   1800             extends BaseFutureTask<T> implements AccountManagerFuture<T> {
   1801         final AccountManagerCallback<T> mCallback;
   1802         public Future2Task(Handler handler, AccountManagerCallback<T> callback) {
   1803             super(handler);
   1804             mCallback = callback;
   1805         }
   1806 
   1807         protected void done() {
   1808             if (mCallback != null) {
   1809                 postRunnableToHandler(new Runnable() {
   1810                     public void run() {
   1811                         mCallback.run(Future2Task.this);
   1812                     }
   1813                 });
   1814             }
   1815         }
   1816 
   1817         public Future2Task<T> start() {
   1818             startTask();
   1819             return this;
   1820         }
   1821 
   1822         private T internalGetResult(Long timeout, TimeUnit unit)
   1823                 throws OperationCanceledException, IOException, AuthenticatorException {
   1824             if (!isDone()) {
   1825                 ensureNotOnMainThread();
   1826             }
   1827             try {
   1828                 if (timeout == null) {
   1829                     return get();
   1830                 } else {
   1831                     return get(timeout, unit);
   1832                 }
   1833             } catch (InterruptedException e) {
   1834                 // fall through and cancel
   1835             } catch (TimeoutException e) {
   1836                 // fall through and cancel
   1837             } catch (CancellationException e) {
   1838                 // fall through and cancel
   1839             } catch (ExecutionException e) {
   1840                 final Throwable cause = e.getCause();
   1841                 if (cause instanceof IOException) {
   1842                     throw (IOException) cause;
   1843                 } else if (cause instanceof UnsupportedOperationException) {
   1844                     throw new AuthenticatorException(cause);
   1845                 } else if (cause instanceof AuthenticatorException) {
   1846                     throw (AuthenticatorException) cause;
   1847                 } else if (cause instanceof RuntimeException) {
   1848                     throw (RuntimeException) cause;
   1849                 } else if (cause instanceof Error) {
   1850                     throw (Error) cause;
   1851                 } else {
   1852                     throw new IllegalStateException(cause);
   1853                 }
   1854             } finally {
   1855                 cancel(true /* interrupt if running */);
   1856             }
   1857             throw new OperationCanceledException();
   1858         }
   1859 
   1860         public T getResult()
   1861                 throws OperationCanceledException, IOException, AuthenticatorException {
   1862             return internalGetResult(null, null);
   1863         }
   1864 
   1865         public T getResult(long timeout, TimeUnit unit)
   1866                 throws OperationCanceledException, IOException, AuthenticatorException {
   1867             return internalGetResult(timeout, unit);
   1868         }
   1869 
   1870     }
   1871 
   1872     private Exception convertErrorToException(int code, String message) {
   1873         if (code == ERROR_CODE_NETWORK_ERROR) {
   1874             return new IOException(message);
   1875         }
   1876 
   1877         if (code == ERROR_CODE_UNSUPPORTED_OPERATION) {
   1878             return new UnsupportedOperationException(message);
   1879         }
   1880 
   1881         if (code == ERROR_CODE_INVALID_RESPONSE) {
   1882             return new AuthenticatorException(message);
   1883         }
   1884 
   1885         if (code == ERROR_CODE_BAD_ARGUMENTS) {
   1886             return new IllegalArgumentException(message);
   1887         }
   1888 
   1889         return new AuthenticatorException(message);
   1890     }
   1891 
   1892     private class GetAuthTokenByTypeAndFeaturesTask
   1893             extends AmsTask implements AccountManagerCallback<Bundle> {
   1894         GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
   1895                 final String[] features, Activity activityForPrompting,
   1896                 final Bundle addAccountOptions, final Bundle loginOptions,
   1897                 AccountManagerCallback<Bundle> callback, Handler handler) {
   1898             super(activityForPrompting, handler, callback);
   1899             if (accountType == null) throw new IllegalArgumentException("account type is null");
   1900             mAccountType = accountType;
   1901             mAuthTokenType = authTokenType;
   1902             mFeatures = features;
   1903             mAddAccountOptions = addAccountOptions;
   1904             mLoginOptions = loginOptions;
   1905             mMyCallback = this;
   1906         }
   1907         volatile AccountManagerFuture<Bundle> mFuture = null;
   1908         final String mAccountType;
   1909         final String mAuthTokenType;
   1910         final String[] mFeatures;
   1911         final Bundle mAddAccountOptions;
   1912         final Bundle mLoginOptions;
   1913         final AccountManagerCallback<Bundle> mMyCallback;
   1914         private volatile int mNumAccounts = 0;
   1915 
   1916         public void doWork() throws RemoteException {
   1917             getAccountsByTypeAndFeatures(mAccountType, mFeatures,
   1918                     new AccountManagerCallback<Account[]>() {
   1919                         public void run(AccountManagerFuture<Account[]> future) {
   1920                             Account[] accounts;
   1921                             try {
   1922                                 accounts = future.getResult();
   1923                             } catch (OperationCanceledException e) {
   1924                                 setException(e);
   1925                                 return;
   1926                             } catch (IOException e) {
   1927                                 setException(e);
   1928                                 return;
   1929                             } catch (AuthenticatorException e) {
   1930                                 setException(e);
   1931                                 return;
   1932                             }
   1933 
   1934                             mNumAccounts = accounts.length;
   1935 
   1936                             if (accounts.length == 0) {
   1937                                 if (mActivity != null) {
   1938                                     // no accounts, add one now. pretend that the user directly
   1939                                     // made this request
   1940                                     mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
   1941                                             mAddAccountOptions, mActivity, mMyCallback, mHandler);
   1942                                 } else {
   1943                                     // send result since we can't prompt to add an account
   1944                                     Bundle result = new Bundle();
   1945                                     result.putString(KEY_ACCOUNT_NAME, null);
   1946                                     result.putString(KEY_ACCOUNT_TYPE, null);
   1947                                     result.putString(KEY_AUTHTOKEN, null);
   1948                                     try {
   1949                                         mResponse.onResult(result);
   1950                                     } catch (RemoteException e) {
   1951                                         // this will never happen
   1952                                     }
   1953                                     // we are done
   1954                                 }
   1955                             } else if (accounts.length == 1) {
   1956                                 // have a single account, return an authtoken for it
   1957                                 if (mActivity == null) {
   1958                                     mFuture = getAuthToken(accounts[0], mAuthTokenType,
   1959                                             false /* notifyAuthFailure */, mMyCallback, mHandler);
   1960                                 } else {
   1961                                     mFuture = getAuthToken(accounts[0],
   1962                                             mAuthTokenType, mLoginOptions,
   1963                                             mActivity, mMyCallback, mHandler);
   1964                                 }
   1965                             } else {
   1966                                 if (mActivity != null) {
   1967                                     IAccountManagerResponse chooseResponse =
   1968                                             new IAccountManagerResponse.Stub() {
   1969                                         public void onResult(Bundle value) throws RemoteException {
   1970                                             Account account = new Account(
   1971                                                     value.getString(KEY_ACCOUNT_NAME),
   1972                                                     value.getString(KEY_ACCOUNT_TYPE));
   1973                                             mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
   1974                                                     mActivity, mMyCallback, mHandler);
   1975                                         }
   1976 
   1977                                         public void onError(int errorCode, String errorMessage)
   1978                                                 throws RemoteException {
   1979                                             mResponse.onError(errorCode, errorMessage);
   1980                                         }
   1981                                     };
   1982                                     // have many accounts, launch the chooser
   1983                                     Intent intent = new Intent();
   1984                                     ComponentName componentName = ComponentName.unflattenFromString(
   1985                                             Resources.getSystem().getString(
   1986                                                     R.string.config_chooseAccountActivity));
   1987                                     intent.setClassName(componentName.getPackageName(),
   1988                                             componentName.getClassName());
   1989                                     intent.putExtra(KEY_ACCOUNTS, accounts);
   1990                                     intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE,
   1991                                             new AccountManagerResponse(chooseResponse));
   1992                                     mActivity.startActivity(intent);
   1993                                     // the result will arrive via the IAccountManagerResponse
   1994                                 } else {
   1995                                     // send result since we can't prompt to select an account
   1996                                     Bundle result = new Bundle();
   1997                                     result.putString(KEY_ACCOUNTS, null);
   1998                                     try {
   1999                                         mResponse.onResult(result);
   2000                                     } catch (RemoteException e) {
   2001                                         // this will never happen
   2002                                     }
   2003                                     // we are done
   2004                                 }
   2005                             }
   2006                         }}, mHandler);
   2007         }
   2008 
   2009         public void run(AccountManagerFuture<Bundle> future) {
   2010             try {
   2011                 final Bundle result = future.getResult();
   2012                 if (mNumAccounts == 0) {
   2013                     final String accountName = result.getString(KEY_ACCOUNT_NAME);
   2014                     final String accountType = result.getString(KEY_ACCOUNT_TYPE);
   2015                     if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
   2016                         setException(new AuthenticatorException("account not in result"));
   2017                         return;
   2018                     }
   2019                     final Account account = new Account(accountName, accountType);
   2020                     mNumAccounts = 1;
   2021                     getAuthToken(account, mAuthTokenType, null /* options */, mActivity,
   2022                             mMyCallback, mHandler);
   2023                     return;
   2024                 }
   2025                 set(result);
   2026             } catch (OperationCanceledException e) {
   2027                 cancel(true /* mayInterruptIfRUnning */);
   2028             } catch (IOException e) {
   2029                 setException(e);
   2030             } catch (AuthenticatorException e) {
   2031                 setException(e);
   2032             }
   2033         }
   2034     }
   2035 
   2036     /**
   2037      * This convenience helper combines the functionality of
   2038      * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and
   2039      * {@link #addAccount}.
   2040      *
   2041      * <p>This method gets a list of the accounts matching the
   2042      * specified type and feature set; if there is exactly one, it is
   2043      * used; if there are more than one, the user is prompted to pick one;
   2044      * if there are none, the user is prompted to add one.  Finally,
   2045      * an auth token is acquired for the chosen account.
   2046      *
   2047      * <p>This method may be called from any thread, but the returned
   2048      * {@link AccountManagerFuture} must not be used on the main thread.
   2049      *
   2050      * <p>This method requires the caller to hold the permission
   2051      * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
   2052      *
   2053      * @param accountType The account type required
   2054      *     (see {@link #getAccountsByType}), must not be null
   2055      * @param authTokenType The desired auth token type
   2056      *     (see {@link #getAuthToken}), must not be null
   2057      * @param features Required features for the account
   2058      *     (see {@link #getAccountsByTypeAndFeatures}), may be null or empty
   2059      * @param activity The {@link Activity} context to use for launching new
   2060      *     sub-Activities to prompt to add an account, select an account,
   2061      *     and/or enter a password, as necessary; used only to call
   2062      *     startActivity(); should not be null
   2063      * @param addAccountOptions Authenticator-specific options to use for
   2064      *     adding new accounts; may be null or empty
   2065      * @param getAuthTokenOptions Authenticator-specific options to use for
   2066      *     getting auth tokens; may be null or empty
   2067      * @param callback Callback to invoke when the request completes,
   2068      *     null for no callback
   2069      * @param handler {@link Handler} identifying the callback thread,
   2070      *     null for the main thread
   2071      * @return An {@link AccountManagerFuture} which resolves to a Bundle with
   2072      *     at least the following fields:
   2073      * <ul>
   2074      * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account
   2075      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
   2076      * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
   2077      * </ul>
   2078      *
   2079      * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
   2080      * <ul>
   2081      * <li> {@link AuthenticatorException} if no authenticator was registered for
   2082      *      this account type or the authenticator failed to respond
   2083      * <li> {@link OperationCanceledException} if the operation was canceled for
   2084      *      any reason, including the user canceling any operation
   2085      * <li> {@link IOException} if the authenticator experienced an I/O problem
   2086      *      updating settings, usually because of network trouble
   2087      * </ul>
   2088      */
   2089     public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
   2090             final String accountType, final String authTokenType, final String[] features,
   2091             final Activity activity, final Bundle addAccountOptions,
   2092             final Bundle getAuthTokenOptions,
   2093             final AccountManagerCallback<Bundle> callback, final Handler handler) {
   2094         if (accountType == null) throw new IllegalArgumentException("account type is null");
   2095         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
   2096         final GetAuthTokenByTypeAndFeaturesTask task =
   2097                 new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
   2098                 activity, addAccountOptions, getAuthTokenOptions, callback, handler);
   2099         task.start();
   2100         return task;
   2101     }
   2102 
   2103     /**
   2104      * Returns an intent to an {@link Activity} that prompts the user to choose from a list of
   2105      * accounts.
   2106      * The caller will then typically start the activity by calling
   2107      * <code>startActivityForResult(intent, ...);</code>.
   2108      * <p>
   2109      * On success the activity returns a Bundle with the account name and type specified using
   2110      * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
   2111      * <p>
   2112      * The most common case is to call this with one account type, e.g.:
   2113      * <p>
   2114      * <pre>  newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null,
   2115      * null, null, null);</pre>
   2116      * @param selectedAccount if specified, indicates that the {@link Account} is the currently
   2117      * selected one, according to the caller's definition of selected.
   2118      * @param allowableAccounts an optional {@link ArrayList} of accounts that are allowed to be
   2119      * shown. If not specified then this field will not limit the displayed accounts.
   2120      * @param allowableAccountTypes an optional string array of account types. These are used
   2121      * both to filter the shown accounts and to filter the list of account types that are shown
   2122      * when adding an account.
   2123      * @param alwaysPromptForAccount if set the account chooser screen is always shown, otherwise
   2124      * it is only shown when there is more than one account from which to choose
   2125      * @param descriptionOverrideText if non-null this string is used as the description in the
   2126      * accounts chooser screen rather than the default
   2127      * @param addAccountAuthTokenType this string is passed as the {@link #addAccount}
   2128      * authTokenType parameter
   2129      * @param addAccountRequiredFeatures this string array is passed as the {@link #addAccount}
   2130      * requiredFeatures parameter
   2131      * @param addAccountOptions This {@link Bundle} is passed as the {@link #addAccount} options
   2132      * parameter
   2133      * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
   2134      */
   2135     static public Intent newChooseAccountIntent(Account selectedAccount,
   2136             ArrayList<Account> allowableAccounts,
   2137             String[] allowableAccountTypes,
   2138             boolean alwaysPromptForAccount,
   2139             String descriptionOverrideText,
   2140             String addAccountAuthTokenType,
   2141             String[] addAccountRequiredFeatures,
   2142             Bundle addAccountOptions) {
   2143         Intent intent = new Intent();
   2144         ComponentName componentName = ComponentName.unflattenFromString(
   2145                 Resources.getSystem().getString(R.string.config_chooseTypeAndAccountActivity));
   2146         intent.setClassName(componentName.getPackageName(),
   2147                 componentName.getClassName());
   2148         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
   2149                 allowableAccounts);
   2150         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
   2151                 allowableAccountTypes);
   2152         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
   2153                 addAccountOptions);
   2154         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount);
   2155         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT,
   2156                 alwaysPromptForAccount);
   2157         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_DESCRIPTION_TEXT_OVERRIDE,
   2158                 descriptionOverrideText);
   2159         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
   2160                 addAccountAuthTokenType);
   2161         intent.putExtra(
   2162                 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
   2163                 addAccountRequiredFeatures);
   2164         return intent;
   2165     }
   2166 
   2167     private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
   2168             Maps.newHashMap();
   2169 
   2170     /**
   2171      * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
   2172      * so that it can read the updated list of accounts and send them to the listener
   2173      * in mAccountsUpdatedListeners.
   2174      */
   2175     private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() {
   2176         public void onReceive(final Context context, final Intent intent) {
   2177             final Account[] accounts = getAccounts();
   2178             // send the result to the listeners
   2179             synchronized (mAccountsUpdatedListeners) {
   2180                 for (Map.Entry<OnAccountsUpdateListener, Handler> entry :
   2181                         mAccountsUpdatedListeners.entrySet()) {
   2182                     postToHandler(entry.getValue(), entry.getKey(), accounts);
   2183                 }
   2184             }
   2185         }
   2186     };
   2187 
   2188     /**
   2189      * Adds an {@link OnAccountsUpdateListener} to this instance of the
   2190      * {@link AccountManager}.  This listener will be notified whenever the
   2191      * list of accounts on the device changes.
   2192      *
   2193      * <p>As long as this listener is present, the AccountManager instance
   2194      * will not be garbage-collected, and neither will the {@link Context}
   2195      * used to retrieve it, which may be a large Activity instance.  To avoid
   2196      * memory leaks, you must remove this listener before then.  Normally
   2197      * listeners are added in an Activity or Service's {@link Activity#onCreate}
   2198      * and removed in {@link Activity#onDestroy}.
   2199      *
   2200      * <p>It is safe to call this method from the main thread.
   2201      *
   2202      * <p>This method requires the caller to hold the permission
   2203      * {@link android.Manifest.permission#GET_ACCOUNTS}.
   2204      *
   2205      * @param listener The listener to send notifications to
   2206      * @param handler {@link Handler} identifying the thread to use
   2207      *     for notifications, null for the main thread
   2208      * @param updateImmediately If true, the listener will be invoked
   2209      *     (on the handler thread) right away with the current account list
   2210      * @throws IllegalArgumentException if listener is null
   2211      * @throws IllegalStateException if listener was already added
   2212      */
   2213     public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
   2214             Handler handler, boolean updateImmediately) {
   2215         if (listener == null) {
   2216             throw new IllegalArgumentException("the listener is null");
   2217         }
   2218         synchronized (mAccountsUpdatedListeners) {
   2219             if (mAccountsUpdatedListeners.containsKey(listener)) {
   2220                 throw new IllegalStateException("this listener is already added");
   2221             }
   2222             final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
   2223 
   2224             mAccountsUpdatedListeners.put(listener, handler);
   2225 
   2226             if (wasEmpty) {
   2227                 // Register a broadcast receiver to monitor account changes
   2228                 IntentFilter intentFilter = new IntentFilter();
   2229                 intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
   2230                 // To recover from disk-full.
   2231                 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
   2232                 mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
   2233             }
   2234         }
   2235 
   2236         if (updateImmediately) {
   2237             postToHandler(handler, listener, getAccounts());
   2238         }
   2239     }
   2240 
   2241     /**
   2242      * Removes an {@link OnAccountsUpdateListener} previously registered with
   2243      * {@link #addOnAccountsUpdatedListener}.  The listener will no longer
   2244      * receive notifications of account changes.
   2245      *
   2246      * <p>It is safe to call this method from the main thread.
   2247      *
   2248      * <p>No permission is required to call this method.
   2249      *
   2250      * @param listener The previously added listener to remove
   2251      * @throws IllegalArgumentException if listener is null
   2252      * @throws IllegalStateException if listener was not already added
   2253      */
   2254     public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
   2255         if (listener == null) throw new IllegalArgumentException("listener is null");
   2256         synchronized (mAccountsUpdatedListeners) {
   2257             if (!mAccountsUpdatedListeners.containsKey(listener)) {
   2258                 Log.e(TAG, "Listener was not previously added");
   2259                 return;
   2260             }
   2261             mAccountsUpdatedListeners.remove(listener);
   2262             if (mAccountsUpdatedListeners.isEmpty()) {
   2263                 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
   2264             }
   2265         }
   2266     }
   2267 }
   2268