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