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