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