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