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