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