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