blob: 150880cd9a633bdb90fbfc6896c031083cefe052 [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.accounts;
18
Fred Quintana60307342009-03-24 22:48:12 -070019import android.app.Activity;
20import android.content.Intent;
21import android.content.Context;
Fred Quintanad9d2f112009-04-23 13:36:27 -070022import android.content.IntentFilter;
23import android.content.BroadcastReceiver;
Costin Manolacheb6437242009-09-10 16:14:12 -070024import android.database.SQLException;
Fred Quintanaa698f422009-04-08 19:14:54 -070025import android.os.Bundle;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.RemoteException;
Fred Quintana33269202009-04-20 16:05:10 -070029import android.os.Parcelable;
Fred Quintana751fdc02010-02-09 14:13:18 -080030import android.os.Build;
Costin Manolacheb6437242009-09-10 16:14:12 -070031import android.util.Log;
Fred Quintanaf0fd8432010-03-08 12:48:05 -080032import android.text.TextUtils;
Fred Quintana60307342009-03-24 22:48:12 -070033
Fred Quintanaa698f422009-04-08 19:14:54 -070034import java.io.IOException;
Fred Quintana1121bb52011-09-14 23:19:35 -070035import java.util.ArrayList;
Fred Quintanaa698f422009-04-08 19:14:54 -070036import java.util.concurrent.Callable;
37import java.util.concurrent.CancellationException;
38import java.util.concurrent.ExecutionException;
39import java.util.concurrent.FutureTask;
40import java.util.concurrent.TimeoutException;
41import java.util.concurrent.TimeUnit;
Fred Quintanad9d2f112009-04-23 13:36:27 -070042import java.util.HashMap;
43import java.util.Map;
44
Fred Quintana1121bb52011-09-14 23:19:35 -070045import com.google.android.collect.Lists;
Fred Quintanad9d2f112009-04-23 13:36:27 -070046import com.google.android.collect.Maps;
Fred Quintana60307342009-03-24 22:48:12 -070047
48/**
Dan Egnor661f0132010-02-19 11:23:00 -080049 * This class provides access to a centralized registry of the user's
Dan Egnor8e4378b2010-08-02 18:22:09 -070050 * online accounts. The user enters credentials (username and password) once
51 * per account, granting applications access to online resources with
52 * "one-click" approval.
Fred Quintana60307342009-03-24 22:48:12 -070053 *
Dan Egnor661f0132010-02-19 11:23:00 -080054 * <p>Different online services have different ways of handling accounts and
55 * authentication, so the account manager uses pluggable <em>authenticator</em>
Dan Egnor8e4378b2010-08-02 18:22:09 -070056 * modules for different <em>account types</em>. Authenticators (which may be
57 * written by third parties) handle the actual details of validating account
58 * credentials and storing account information. For example, Google, Facebook,
59 * and Microsoft Exchange each have their own authenticator.
Dan Egnor661f0132010-02-19 11:23:00 -080060 *
61 * <p>Many servers support some notion of an <em>authentication token</em>,
62 * which can be used to authenticate a request to the server without sending
63 * the user's actual password. (Auth tokens are normally created with a
64 * separate request which does include the user's credentials.) AccountManager
Dan Egnor8e4378b2010-08-02 18:22:09 -070065 * can generate auth tokens for applications, so the application doesn't need to
66 * handle passwords directly. Auth tokens are normally reusable and cached by
67 * AccountManager, but must be refreshed periodically. It's the responsibility
68 * of applications to <em>invalidate</em> auth tokens when they stop working so
69 * the AccountManager knows it needs to regenerate them.
Dan Egnor661f0132010-02-19 11:23:00 -080070 *
71 * <p>Applications accessing a server normally go through these steps:
72 *
73 * <ul>
74 * <li>Get an instance of AccountManager using {@link #get(Context)}.
75 *
76 * <li>List the available accounts using {@link #getAccountsByType} or
77 * {@link #getAccountsByTypeAndFeatures}. Normally applications will only
78 * be interested in accounts with one particular <em>type</em>, which
79 * identifies the authenticator. Account <em>features</em> are used to
80 * identify particular account subtypes and capabilities. Both the account
81 * type and features are authenticator-specific strings, and must be known by
82 * the application in coordination with its preferred authenticators.
83 *
84 * <li>Select one or more of the available accounts, possibly by asking the
85 * user for their preference. If no suitable accounts are available,
86 * {@link #addAccount} may be called to prompt the user to create an
87 * account of the appropriate type.
88 *
Dan Egnor8e4378b2010-08-02 18:22:09 -070089 * <li><b>Important:</b> If the application is using a previously remembered
90 * account selection, it must make sure the account is still in the list
91 * of accounts returned by {@link #getAccountsByType}. Requesting an auth token
92 * for an account no longer on the device results in an undefined failure.
93 *
Dan Egnor661f0132010-02-19 11:23:00 -080094 * <li>Request an auth token for the selected account(s) using one of the
95 * {@link #getAuthToken} methods or related helpers. Refer to the description
96 * of each method for exact usage and error handling details.
97 *
98 * <li>Make the request using the auth token. The form of the auth token,
99 * the format of the request, and the protocol used are all specific to the
Dan Egnor8e4378b2010-08-02 18:22:09 -0700100 * service you are accessing. The application may use whatever network and
101 * protocol libraries are useful.
Dan Egnor661f0132010-02-19 11:23:00 -0800102 *
103 * <li><b>Important:</b> If the request fails with an authentication error,
104 * it could be that a cached auth token is stale and no longer honored by
105 * the server. The application must call {@link #invalidateAuthToken} to remove
106 * the token from the cache, otherwise requests will continue failing! After
107 * invalidating the auth token, immediately go back to the "Request an auth
108 * token" step above. If the process fails the second time, then it can be
109 * treated as a "genuine" authentication failure and the user notified or other
110 * appropriate actions taken.
111 * </ul>
112 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700113 * <p>Some AccountManager methods may need to interact with the user to
Dan Egnor661f0132010-02-19 11:23:00 -0800114 * prompt for credentials, present options, or ask the user to add an account.
115 * The caller may choose whether to allow AccountManager to directly launch the
116 * necessary user interface and wait for the user, or to return an Intent which
117 * the caller may use to launch the interface, or (in some cases) to install a
118 * notification which the user can select at any time to launch the interface.
119 * To have AccountManager launch the interface directly, the caller must supply
120 * the current foreground {@link Activity} context.
121 *
122 * <p>Many AccountManager methods take {@link AccountManagerCallback} and
Dan Egnor8e4378b2010-08-02 18:22:09 -0700123 * {@link Handler} as parameters. These methods return immediately and
Dan Egnor661f0132010-02-19 11:23:00 -0800124 * run asynchronously. If a callback is provided then
125 * {@link AccountManagerCallback#run} will be invoked on the Handler's
126 * thread when the request completes, successfully or not.
Dan Egnor8e4378b2010-08-02 18:22:09 -0700127 * The result is retrieved by calling {@link AccountManagerFuture#getResult()}
128 * on the {@link AccountManagerFuture} returned by the method (and also passed
129 * to the callback). This method waits for the operation to complete (if
130 * necessary) and either returns the result or throws an exception if an error
131 * occurred during the operation. To make the request synchronously, call
Dan Egnor661f0132010-02-19 11:23:00 -0800132 * {@link AccountManagerFuture#getResult()} immediately on receiving the
Dan Egnor8e4378b2010-08-02 18:22:09 -0700133 * future from the method; no callback need be supplied.
Dan Egnor661f0132010-02-19 11:23:00 -0800134 *
135 * <p>Requests which may block, including
136 * {@link AccountManagerFuture#getResult()}, must never be called on
137 * the application's main event thread. These operations throw
138 * {@link IllegalStateException} if they are used on the main thread.
Fred Quintana60307342009-03-24 22:48:12 -0700139 */
140public class AccountManager {
141 private static final String TAG = "AccountManager";
142
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700143 public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
144 public static final int ERROR_CODE_NETWORK_ERROR = 3;
145 public static final int ERROR_CODE_CANCELED = 4;
146 public static final int ERROR_CODE_INVALID_RESPONSE = 5;
147 public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
148 public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
149 public static final int ERROR_CODE_BAD_REQUEST = 8;
Fred Quintana756b7352009-10-21 13:43:10 -0700150
Dan Egnor661f0132010-02-19 11:23:00 -0800151 /**
Dan Egnor8e4378b2010-08-02 18:22:09 -0700152 * Bundle key used for the {@link String} account name in results
Dan Egnor661f0132010-02-19 11:23:00 -0800153 * from methods which return information about a particular account.
154 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700155 public static final String KEY_ACCOUNT_NAME = "authAccount";
Dan Egnor661f0132010-02-19 11:23:00 -0800156
157 /**
Dan Egnor8e4378b2010-08-02 18:22:09 -0700158 * Bundle key used for the {@link String} account type in results
Dan Egnor661f0132010-02-19 11:23:00 -0800159 * from methods which return information about a particular account.
160 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700161 public static final String KEY_ACCOUNT_TYPE = "accountType";
Dan Egnor661f0132010-02-19 11:23:00 -0800162
163 /**
Dan Egnor8e4378b2010-08-02 18:22:09 -0700164 * Bundle key used for the auth token value in results
Dan Egnor661f0132010-02-19 11:23:00 -0800165 * from {@link #getAuthToken} and friends.
166 */
167 public static final String KEY_AUTHTOKEN = "authtoken";
168
169 /**
Dan Egnor8e4378b2010-08-02 18:22:09 -0700170 * Bundle key used for an {@link Intent} in results from methods that
Dan Egnor661f0132010-02-19 11:23:00 -0800171 * may require the caller to interact with the user. The Intent can
172 * be used to start the corresponding user interface activity.
173 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700174 public static final String KEY_INTENT = "intent";
Dan Egnor661f0132010-02-19 11:23:00 -0800175
176 /**
Dan Egnor8e4378b2010-08-02 18:22:09 -0700177 * Bundle key used to supply the password directly in options to
Dan Egnor661f0132010-02-19 11:23:00 -0800178 * {@link #confirmCredentials}, rather than prompting the user with
179 * the standard password prompt.
180 */
181 public static final String KEY_PASSWORD = "password";
182
183 public static final String KEY_ACCOUNTS = "accounts";
Brian Carlstrom46703b02011-04-06 15:41:29 -0700184
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700185 public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
186 public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
Dan Egnor661f0132010-02-19 11:23:00 -0800187 public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700188 public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage";
189 public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey";
Dan Egnor661f0132010-02-19 11:23:00 -0800190 public static final String KEY_BOOLEAN_RESULT = "booleanResult";
191 public static final String KEY_ERROR_CODE = "errorCode";
192 public static final String KEY_ERROR_MESSAGE = "errorMessage";
193 public static final String KEY_USERDATA = "userdata";
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700194
Costin Manolachea40c6302010-12-13 14:50:45 -0800195 /**
196 * Authenticators using 'customTokens' option will also get the UID of the
197 * caller
198 */
199 public static final String KEY_CALLER_UID = "callerUid";
200 public static final String KEY_CALLER_PID = "callerPid";
Dan Egnor661f0132010-02-19 11:23:00 -0800201
Costin Manolached6060452011-01-24 16:11:36 -0800202 /**
Fred Quintanaad93a322011-09-08 13:21:01 -0700203 * The Android package of the caller will be set in the options bundle by the
204 * {@link AccountManager} and will be passed to the AccountManagerService and
205 * to the AccountAuthenticators. The uid of the caller will be known by the
206 * AccountManagerService as well as the AccountAuthenticators so they will be able to
207 * verify that the package is consistent with the uid (a uid might be shared by many
208 * packages).
209 */
210 public static final String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
211
212 /**
Costin Manolached6060452011-01-24 16:11:36 -0800213 * Boolean, if set and 'customTokens' the authenticator is responsible for
214 * notifications.
215 * @hide
216 */
217 public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
218
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700219 public static final String ACTION_AUTHENTICATOR_INTENT =
220 "android.accounts.AccountAuthenticator";
221 public static final String AUTHENTICATOR_META_DATA_NAME =
Dan Egnor661f0132010-02-19 11:23:00 -0800222 "android.accounts.AccountAuthenticator";
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700223 public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
224
Fred Quintana60307342009-03-24 22:48:12 -0700225 private final Context mContext;
226 private final IAccountManager mService;
Fred Quintanad9d2f112009-04-23 13:36:27 -0700227 private final Handler mMainHandler;
Dan Egnor661f0132010-02-19 11:23:00 -0800228
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700229 /**
230 * Action sent as a broadcast Intent by the AccountsService
Dan Egnor661f0132010-02-19 11:23:00 -0800231 * when accounts are added, accounts are removed, or an
232 * account's credentials (saved password, etc) are changed.
233 *
234 * @see #addOnAccountsUpdatedListener
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700235 */
236 public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
237 "android.accounts.LOGIN_ACCOUNTS_CHANGED";
Fred Quintana60307342009-03-24 22:48:12 -0700238
Fred Quintana33269202009-04-20 16:05:10 -0700239 /**
240 * @hide
241 */
Fred Quintana60307342009-03-24 22:48:12 -0700242 public AccountManager(Context context, IAccountManager service) {
243 mContext = context;
244 mService = service;
Fred Quintanad9d2f112009-04-23 13:36:27 -0700245 mMainHandler = new Handler(mContext.getMainLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700246 }
247
Fred Quintana0eabf022009-04-27 15:08:17 -0700248 /**
249 * @hide used for testing only
250 */
251 public AccountManager(Context context, IAccountManager service, Handler handler) {
252 mContext = context;
253 mService = service;
254 mMainHandler = handler;
255 }
256
Fred Quintana756b7352009-10-21 13:43:10 -0700257 /**
Fred Quintanaf0fd8432010-03-08 12:48:05 -0800258 * @hide for internal use only
259 */
260 public static Bundle sanitizeResult(Bundle result) {
Fred Quintana382601f2010-03-25 12:25:10 -0700261 if (result != null) {
262 if (result.containsKey(KEY_AUTHTOKEN)
263 && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) {
264 final Bundle newResult = new Bundle(result);
265 newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>");
266 return newResult;
267 }
Fred Quintanaf0fd8432010-03-08 12:48:05 -0800268 }
269 return result;
270 }
271
272 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800273 * Gets an AccountManager instance associated with a Context.
274 * The {@link Context} will be used as long as the AccountManager is
275 * active, so make sure to use a {@link Context} whose lifetime is
276 * commensurate with any listeners registered to
277 * {@link #addOnAccountsUpdatedListener} or similar methods.
278 *
279 * <p>It is safe to call this method from the main thread.
280 *
281 * <p>No permission is required to call this method.
282 *
Fred Quintana756b7352009-10-21 13:43:10 -0700283 * @param context The {@link Context} to use when necessary
Dan Egnor661f0132010-02-19 11:23:00 -0800284 * @return An {@link AccountManager} instance
Fred Quintana756b7352009-10-21 13:43:10 -0700285 */
Fred Quintanaa698f422009-04-08 19:14:54 -0700286 public static AccountManager get(Context context) {
Fred Quintana382601f2010-03-25 12:25:10 -0700287 if (context == null) throw new IllegalArgumentException("context is null");
Fred Quintanaa698f422009-04-08 19:14:54 -0700288 return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
289 }
290
Fred Quintana756b7352009-10-21 13:43:10 -0700291 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800292 * Gets the saved password associated with the account.
293 * This is intended for authenticators and related code; applications
294 * should get an auth token instead.
295 *
296 * <p>It is safe to call this method from the main thread.
297 *
298 * <p>This method requires the caller to hold the permission
299 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
300 * and to have the same UID as the account's authenticator.
301 *
302 * @param account The account to query for a password
303 * @return The account's password, null if none or if the account doesn't exist
Fred Quintana756b7352009-10-21 13:43:10 -0700304 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700305 public String getPassword(final Account account) {
Fred Quintana382601f2010-03-25 12:25:10 -0700306 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana60307342009-03-24 22:48:12 -0700307 try {
308 return mService.getPassword(account);
309 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700310 // will never happen
Fred Quintana60307342009-03-24 22:48:12 -0700311 throw new RuntimeException(e);
312 }
313 }
314
Fred Quintana756b7352009-10-21 13:43:10 -0700315 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800316 * Gets the user data named by "key" associated with the account.
317 * This is intended for authenticators and related code to store
318 * arbitrary metadata along with accounts. The meaning of the keys
319 * and values is up to the authenticator for the account.
320 *
321 * <p>It is safe to call this method from the main thread.
322 *
323 * <p>This method requires the caller to hold the permission
324 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
325 * and to have the same UID as the account's authenticator.
326 *
327 * @param account The account to query for user data
328 * @return The user data, null if the account or key doesn't exist
Fred Quintana756b7352009-10-21 13:43:10 -0700329 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700330 public String getUserData(final Account account, final String key) {
Fred Quintana382601f2010-03-25 12:25:10 -0700331 if (account == null) throw new IllegalArgumentException("account is null");
332 if (key == null) throw new IllegalArgumentException("key is null");
Fred Quintana60307342009-03-24 22:48:12 -0700333 try {
334 return mService.getUserData(account, key);
335 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700336 // will never happen
Fred Quintana60307342009-03-24 22:48:12 -0700337 throw new RuntimeException(e);
338 }
339 }
340
Fred Quintana756b7352009-10-21 13:43:10 -0700341 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800342 * Lists the currently registered authenticators.
343 *
344 * <p>It is safe to call this method from the main thread.
345 *
346 * <p>No permission is required to call this method.
347 *
348 * @return An array of {@link AuthenticatorDescription} for every
349 * authenticator known to the AccountManager service. Empty (never
350 * null) if no authenticators are known.
Fred Quintana756b7352009-10-21 13:43:10 -0700351 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700352 public AuthenticatorDescription[] getAuthenticatorTypes() {
Fred Quintanaa698f422009-04-08 19:14:54 -0700353 try {
354 return mService.getAuthenticatorTypes();
355 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700356 // will never happen
Fred Quintanaa698f422009-04-08 19:14:54 -0700357 throw new RuntimeException(e);
358 }
359 }
360
Fred Quintana756b7352009-10-21 13:43:10 -0700361 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800362 * Lists all accounts of any type registered on the device.
363 * Equivalent to getAccountsByType(null).
364 *
365 * <p>It is safe to call this method from the main thread.
366 *
367 * <p>This method requires the caller to hold the permission
368 * {@link android.Manifest.permission#GET_ACCOUNTS}.
369 *
370 * @return An array of {@link Account}, one for each account. Empty
371 * (never null) if no accounts have been added.
Fred Quintana756b7352009-10-21 13:43:10 -0700372 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700373 public Account[] getAccounts() {
Fred Quintana60307342009-03-24 22:48:12 -0700374 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700375 return mService.getAccounts(null);
Fred Quintana60307342009-03-24 22:48:12 -0700376 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700377 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700378 throw new RuntimeException(e);
379 }
380 }
381
Fred Quintana756b7352009-10-21 13:43:10 -0700382 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800383 * Lists all accounts of a particular type. The account type is a
384 * string token corresponding to the authenticator and useful domain
385 * of the account. For example, there are types corresponding to Google
386 * and Facebook. The exact string token to use will be published somewhere
387 * associated with the authenticator in question.
388 *
389 * <p>It is safe to call this method from the main thread.
390 *
391 * <p>This method requires the caller to hold the permission
392 * {@link android.Manifest.permission#GET_ACCOUNTS}.
393 *
394 * @param type The type of accounts to return, null to retrieve all accounts
395 * @return An array of {@link Account}, one per matching account. Empty
396 * (never null) if no accounts of the specified type have been added.
Fred Quintana756b7352009-10-21 13:43:10 -0700397 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700398 public Account[] getAccountsByType(String type) {
Fred Quintana60307342009-03-24 22:48:12 -0700399 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700400 return mService.getAccounts(type);
Fred Quintana60307342009-03-24 22:48:12 -0700401 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700402 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700403 throw new RuntimeException(e);
404 }
405 }
406
Fred Quintana756b7352009-10-21 13:43:10 -0700407 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800408 * Finds out whether a particular account has all the specified features.
409 * Account features are authenticator-specific string tokens identifying
410 * boolean account properties. For example, features are used to tell
411 * whether Google accounts have a particular service (such as Google
412 * Calendar or Google Talk) enabled. The feature names and their meanings
413 * are published somewhere associated with the authenticator in question.
414 *
415 * <p>This method may be called from any thread, but the returned
416 * {@link AccountManagerFuture} must not be used on the main thread.
417 *
418 * <p>This method requires the caller to hold the permission
419 * {@link android.Manifest.permission#GET_ACCOUNTS}.
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800420 *
421 * @param account The {@link Account} to test
Dan Egnor661f0132010-02-19 11:23:00 -0800422 * @param features An array of the account features to check
423 * @param callback Callback to invoke when the request completes,
424 * null for no callback
425 * @param handler {@link Handler} identifying the callback thread,
426 * null for the main thread
427 * @return An {@link AccountManagerFuture} which resolves to a Boolean,
428 * true if the account exists and has all of the specified features.
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800429 */
Fred Quintana3084a6f2010-01-14 18:02:03 -0800430 public AccountManagerFuture<Boolean> hasFeatures(final Account account,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800431 final String[] features,
432 AccountManagerCallback<Boolean> callback, Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -0700433 if (account == null) throw new IllegalArgumentException("account is null");
434 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800435 return new Future2Task<Boolean>(handler, callback) {
436 public void doWork() throws RemoteException {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800437 mService.hasFeatures(mResponse, account, features);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800438 }
439 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
440 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
441 throw new AuthenticatorException("no result in response");
442 }
443 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
444 }
445 }.start();
446 }
447
448 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800449 * Lists all accounts of a type which have certain features. The account
450 * type identifies the authenticator (see {@link #getAccountsByType}).
451 * Account features are authenticator-specific string tokens identifying
452 * boolean account properties (see {@link #hasFeatures}).
Fred Quintana756b7352009-10-21 13:43:10 -0700453 *
Dan Egnor661f0132010-02-19 11:23:00 -0800454 * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator,
455 * which may contact the server or do other work to check account features,
456 * so the method returns an {@link AccountManagerFuture}.
Fred Quintanaa698f422009-04-08 19:14:54 -0700457 *
Dan Egnor661f0132010-02-19 11:23:00 -0800458 * <p>This method may be called from any thread, but the returned
459 * {@link AccountManagerFuture} must not be used on the main thread.
Fred Quintana756b7352009-10-21 13:43:10 -0700460 *
Dan Egnor661f0132010-02-19 11:23:00 -0800461 * <p>This method requires the caller to hold the permission
462 * {@link android.Manifest.permission#GET_ACCOUNTS}.
Fred Quintana756b7352009-10-21 13:43:10 -0700463 *
Dan Egnor661f0132010-02-19 11:23:00 -0800464 * @param type The type of accounts to return, must not be null
465 * @param features An array of the account features to require,
466 * may be null or empty
467 * @param callback Callback to invoke when the request completes,
468 * null for no callback
469 * @param handler {@link Handler} identifying the callback thread,
470 * null for the main thread
471 * @return An {@link AccountManagerFuture} which resolves to an array of
472 * {@link Account}, one per account of the specified type which
473 * matches the requested features.
Fred Quintana8570f742010-02-18 10:32:54 -0800474 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700475 public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
476 final String type, final String[] features,
477 AccountManagerCallback<Account[]> callback, Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -0700478 if (type == null) throw new IllegalArgumentException("type is null");
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700479 return new Future2Task<Account[]>(handler, callback) {
480 public void doWork() throws RemoteException {
481 mService.getAccountsByFeatures(mResponse, type, features);
482 }
483 public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700484 if (!bundle.containsKey(KEY_ACCOUNTS)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700485 throw new AuthenticatorException("no result in response");
486 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700487 final Parcelable[] parcelables = bundle.getParcelableArray(KEY_ACCOUNTS);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700488 Account[] descs = new Account[parcelables.length];
489 for (int i = 0; i < parcelables.length; i++) {
490 descs[i] = (Account) parcelables[i];
491 }
492 return descs;
493 }
494 }.start();
495 }
496
Fred Quintana756b7352009-10-21 13:43:10 -0700497 /**
Dan Egnor661f0132010-02-19 11:23:00 -0800498 * Adds an account directly to the AccountManager. Normally used by sign-up
499 * wizards associated with authenticators, not directly by applications.
Fred Quintana756b7352009-10-21 13:43:10 -0700500 *
Dan Egnor661f0132010-02-19 11:23:00 -0800501 * <p>It is safe to call this method from the main thread.
502 *
503 * <p>This method requires the caller to hold the permission
504 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
505 * and to have the same UID as the added account's authenticator.
506 *
507 * @param account The {@link Account} to add
508 * @param password The password to associate with the account, null for none
509 * @param userdata String values to use for the account's userdata, null for none
Dan Egnor8e4378b2010-08-02 18:22:09 -0700510 * @return True if the account was successfully added, false if the account
Dan Egnor661f0132010-02-19 11:23:00 -0800511 * already exists, the account is null, or another error occurs.
512 */
513 public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
Fred Quintana382601f2010-03-25 12:25:10 -0700514 if (account == null) throw new IllegalArgumentException("account is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800515 try {
516 return mService.addAccount(account, password, userdata);
517 } catch (RemoteException e) {
518 // won't ever happen
519 throw new RuntimeException(e);
520 }
521 }
522
523 /**
524 * Removes an account from the AccountManager. Does nothing if the account
525 * does not exist. Does not delete the account from the server.
526 * The authenticator may have its own policies preventing account
527 * deletion, in which case the account will not be deleted.
528 *
529 * <p>This method may be called from any thread, but the returned
530 * {@link AccountManagerFuture} must not be used on the main thread.
531 *
532 * <p>This method requires the caller to hold the permission
533 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
534 *
535 * @param account The {@link Account} to remove
536 * @param callback Callback to invoke when the request completes,
537 * null for no callback
538 * @param handler {@link Handler} identifying the callback thread,
539 * null for the main thread
540 * @return An {@link AccountManagerFuture} which resolves to a Boolean,
541 * true if the account has been successfully removed,
542 * false if the authenticator forbids deleting this account.
543 */
544 public AccountManagerFuture<Boolean> removeAccount(final Account account,
545 AccountManagerCallback<Boolean> callback, Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -0700546 if (account == null) throw new IllegalArgumentException("account is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800547 return new Future2Task<Boolean>(handler, callback) {
548 public void doWork() throws RemoteException {
549 mService.removeAccount(mResponse, account);
550 }
551 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
552 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
553 throw new AuthenticatorException("no result in response");
554 }
555 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
556 }
557 }.start();
558 }
559
560 /**
561 * Removes an auth token from the AccountManager's cache. Does nothing if
562 * the auth token is not currently in the cache. Applications must call this
563 * method when the auth token is found to have expired or otherwise become
564 * invalid for authenticating requests. The AccountManager does not validate
565 * or expire cached auth tokens otherwise.
566 *
567 * <p>It is safe to call this method from the main thread.
568 *
569 * <p>This method requires the caller to hold the permission
Fred Quintanab38eb142010-02-24 13:40:54 -0800570 * {@link android.Manifest.permission#MANAGE_ACCOUNTS} or
571 * {@link android.Manifest.permission#USE_CREDENTIALS}
Dan Egnor661f0132010-02-19 11:23:00 -0800572 *
Fred Quintanaf35b68f2010-04-01 11:36:00 -0700573 * @param accountType The account type of the auth token to invalidate, must not be null
574 * @param authToken The auth token to invalidate, may be null
Dan Egnor661f0132010-02-19 11:23:00 -0800575 */
576 public void invalidateAuthToken(final String accountType, final String authToken) {
Fred Quintana382601f2010-03-25 12:25:10 -0700577 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800578 try {
Fred Quintanaf35b68f2010-04-01 11:36:00 -0700579 if (authToken != null) {
580 mService.invalidateAuthToken(accountType, authToken);
581 }
Dan Egnor661f0132010-02-19 11:23:00 -0800582 } catch (RemoteException e) {
583 // won't ever happen
584 throw new RuntimeException(e);
585 }
586 }
587
588 /**
589 * Gets an auth token from the AccountManager's cache. If no auth
590 * token is cached for this account, null will be returned -- a new
591 * auth token will not be generated, and the server will not be contacted.
592 * Intended for use by the authenticator, not directly by applications.
593 *
594 * <p>It is safe to call this method from the main thread.
595 *
596 * <p>This method requires the caller to hold the permission
597 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
598 * and to have the same UID as the account's authenticator.
599 *
600 * @param account The account to fetch an auth token for
601 * @param authTokenType The type of auth token to fetch, see {#getAuthToken}
602 * @return The cached auth token for this account and type, or null if
603 * no auth token is cached or the account does not exist.
604 */
605 public String peekAuthToken(final Account account, final String authTokenType) {
Fred Quintana382601f2010-03-25 12:25:10 -0700606 if (account == null) throw new IllegalArgumentException("account is null");
607 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800608 try {
609 return mService.peekAuthToken(account, authTokenType);
610 } catch (RemoteException e) {
611 // won't ever happen
612 throw new RuntimeException(e);
613 }
614 }
615
616 /**
617 * Sets or forgets a saved password. This modifies the local copy of the
618 * password used to automatically authenticate the user; it does
619 * not change the user's account password on the server. Intended for use
620 * by the authenticator, not directly by applications.
621 *
622 * <p>It is safe to call this method from the main thread.
623 *
624 * <p>This method requires the caller to hold the permission
625 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
626 * and have the same UID as the account's authenticator.
627 *
628 * @param account The account to set a password for
629 * @param password The password to set, null to clear the password
630 */
631 public void setPassword(final Account account, final String password) {
Fred Quintana382601f2010-03-25 12:25:10 -0700632 if (account == null) throw new IllegalArgumentException("account is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800633 try {
634 mService.setPassword(account, password);
635 } catch (RemoteException e) {
636 // won't ever happen
637 throw new RuntimeException(e);
638 }
639 }
640
641 /**
642 * Forgets a saved password. This erases the local copy of the password;
643 * it does not change the user's account password on the server.
644 * Has the same effect as setPassword(account, null) but requires fewer
645 * permissions, and may be used by applications or management interfaces
646 * to "sign out" from an account.
647 *
648 * <p>It is safe to call this method from the main thread.
649 *
650 * <p>This method requires the caller to hold the permission
651 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}
652 *
653 * @param account The account whose password to clear
654 */
655 public void clearPassword(final Account account) {
Fred Quintana382601f2010-03-25 12:25:10 -0700656 if (account == null) throw new IllegalArgumentException("account is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800657 try {
658 mService.clearPassword(account);
659 } catch (RemoteException e) {
660 // won't ever happen
661 throw new RuntimeException(e);
662 }
663 }
664
665 /**
666 * Sets one userdata key for an account. Intended by use for the
667 * authenticator to stash state for itself, not directly by applications.
668 * The meaning of the keys and values is up to the authenticator.
669 *
670 * <p>It is safe to call this method from the main thread.
671 *
672 * <p>This method requires the caller to hold the permission
673 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
674 * and to have the same UID as the account's authenticator.
675 *
676 * @param account The account to set the userdata for
677 * @param key The userdata key to set. Must not be null
678 * @param value The value to set, null to clear this userdata key
679 */
680 public void setUserData(final Account account, final String key, final String value) {
Fred Quintana382601f2010-03-25 12:25:10 -0700681 if (account == null) throw new IllegalArgumentException("account is null");
682 if (key == null) throw new IllegalArgumentException("key is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800683 try {
684 mService.setUserData(account, key, value);
685 } catch (RemoteException e) {
686 // won't ever happen
687 throw new RuntimeException(e);
688 }
689 }
690
691 /**
692 * Adds an auth token to the AccountManager cache for an account.
693 * If the account does not exist then this call has no effect.
694 * Replaces any previous auth token for this account and auth token type.
695 * Intended for use by the authenticator, not directly by applications.
696 *
697 * <p>It is safe to call this method from the main thread.
698 *
699 * <p>This method requires the caller to hold the permission
700 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
701 * and to have the same UID as the account's authenticator.
702 *
703 * @param account The account to set an auth token for
704 * @param authTokenType The type of the auth token, see {#getAuthToken}
705 * @param authToken The auth token to add to the cache
706 */
707 public void setAuthToken(Account account, final String authTokenType, final String authToken) {
Fred Quintana382601f2010-03-25 12:25:10 -0700708 if (account == null) throw new IllegalArgumentException("account is null");
709 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800710 try {
711 mService.setAuthToken(account, authTokenType, authToken);
712 } catch (RemoteException e) {
713 // won't ever happen
714 throw new RuntimeException(e);
715 }
716 }
717
718 /**
719 * This convenience helper synchronously gets an auth token with
720 * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}.
721 *
722 * <p>This method may block while a network request completes, and must
723 * never be made from the main thread.
724 *
725 * <p>This method requires the caller to hold the permission
726 * {@link android.Manifest.permission#USE_CREDENTIALS}.
727 *
728 * @param account The account to fetch an auth token for
729 * @param authTokenType The auth token type, see {#link getAuthToken}
730 * @param notifyAuthFailure If true, display a notification and return null
731 * if authentication fails; if false, prompt and wait for the user to
732 * re-enter correct credentials before returning
733 * @return An auth token of the specified type for this account, or null
734 * if authentication fails or none can be fetched.
735 * @throws AuthenticatorException if the authenticator failed to respond
736 * @throws OperationCanceledException if the request was canceled for any
737 * reason, including the user canceling a credential request
738 * @throws java.io.IOException if the authenticator experienced an I/O problem
739 * creating a new auth token, usually because of network trouble
740 */
741 public String blockingGetAuthToken(Account account, String authTokenType,
742 boolean notifyAuthFailure)
743 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintana382601f2010-03-25 12:25:10 -0700744 if (account == null) throw new IllegalArgumentException("account is null");
745 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800746 Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
747 null /* handler */).getResult();
Fred Quintana96580e02010-03-04 13:42:42 -0800748 if (bundle == null) {
749 // This should never happen, but it does, occasionally. If it does return null to
750 // signify that we were not able to get the authtoken.
751 // TODO: remove this when the bug is found that sometimes causes a null bundle to be
752 // returned
753 Log.e(TAG, "blockingGetAuthToken: null was returned from getResult() for "
754 + account + ", authTokenType " + authTokenType);
755 return null;
756 }
Dan Egnor661f0132010-02-19 11:23:00 -0800757 return bundle.getString(KEY_AUTHTOKEN);
758 }
759
760 /**
761 * Gets an auth token of the specified type for a particular account,
762 * prompting the user for credentials if necessary. This method is
763 * intended for applications running in the foreground where it makes
764 * sense to ask the user directly for a password.
765 *
766 * <p>If a previously generated auth token is cached for this account and
Dan Egnor8e4378b2010-08-02 18:22:09 -0700767 * type, then it is returned. Otherwise, if a saved password is
768 * available, it is sent to the server to generate a new auth token.
769 * Otherwise, the user is prompted to enter a password.
Dan Egnor661f0132010-02-19 11:23:00 -0800770 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700771 * <p>Some authenticators have auth token <em>types</em>, whose value
772 * is authenticator-dependent. Some services use different token types to
773 * access different functionality -- for example, Google uses different auth
774 * tokens to access Gmail and Google Calendar for the same account.
Dan Egnor661f0132010-02-19 11:23:00 -0800775 *
776 * <p>This method may be called from any thread, but the returned
777 * {@link AccountManagerFuture} must not be used on the main thread.
778 *
779 * <p>This method requires the caller to hold the permission
780 * {@link android.Manifest.permission#USE_CREDENTIALS}.
781 *
782 * @param account The account to fetch an auth token for
783 * @param authTokenType The auth token type, an authenticator-dependent
784 * string token, must not be null
785 * @param options Authenticator-specific options for the request,
786 * may be null or empty
787 * @param activity The {@link Activity} context to use for launching a new
788 * authenticator-defined sub-Activity to prompt the user for a password
789 * if necessary; used only to call startActivity(); must not be null.
790 * @param callback Callback to invoke when the request completes,
791 * null for no callback
792 * @param handler {@link Handler} identifying the callback thread,
793 * null for the main thread
794 * @return An {@link AccountManagerFuture} which resolves to a Bundle with
795 * at least the following fields:
Fred Quintana756b7352009-10-21 13:43:10 -0700796 * <ul>
Dan Egnor661f0132010-02-19 11:23:00 -0800797 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
798 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
799 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
Fred Quintana756b7352009-10-21 13:43:10 -0700800 * </ul>
Dan Egnor661f0132010-02-19 11:23:00 -0800801 *
802 * (Other authenticator-specific values may be returned.) If an auth token
803 * could not be fetched, {@link AccountManagerFuture#getResult()} throws:
804 * <ul>
805 * <li> {@link AuthenticatorException} if the authenticator failed to respond
806 * <li> {@link OperationCanceledException} if the operation is canceled for
807 * any reason, incluidng the user canceling a credential request
808 * <li> {@link IOException} if the authenticator experienced an I/O problem
809 * creating a new auth token, usually because of network trouble
810 * </ul>
Dan Egnor8e4378b2010-08-02 18:22:09 -0700811 * If the account is no longer present on the device, the return value is
812 * authenticator-dependent. The caller should verify the validity of the
813 * account before requesting an auth token.
Dan Egnor661f0132010-02-19 11:23:00 -0800814 */
815 public AccountManagerFuture<Bundle> getAuthToken(
816 final Account account, final String authTokenType, final Bundle options,
817 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -0700818 if (account == null) throw new IllegalArgumentException("account is null");
Dan Egnor661f0132010-02-19 11:23:00 -0800819 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Costin Manolachee5847ad2011-09-14 12:52:19 -0700820 final Bundle optionsIn = new Bundle();
821 if (options != null) {
822 optionsIn.putAll(options);
823 }
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700824 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
Dan Egnor661f0132010-02-19 11:23:00 -0800825 return new AmsTask(activity, handler, callback) {
826 public void doWork() throws RemoteException {
827 mService.getAuthToken(mResponse, account, authTokenType,
828 false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700829 optionsIn);
Dan Egnor661f0132010-02-19 11:23:00 -0800830 }
831 }.start();
832 }
833
834 /**
835 * Gets an auth token of the specified type for a particular account,
836 * optionally raising a notification if the user must enter credentials.
837 * This method is intended for background tasks and services where the
838 * user should not be immediately interrupted with a password prompt.
839 *
840 * <p>If a previously generated auth token is cached for this account and
Dan Egnor8e4378b2010-08-02 18:22:09 -0700841 * type, then it is returned. Otherwise, if a saved password is
842 * available, it is sent to the server to generate a new auth token.
843 * Otherwise, an {@link Intent} is returned which, when started, will
844 * prompt the user for a password. If the notifyAuthFailure parameter is
845 * set, a status bar notification is also created with the same Intent,
Dan Egnor661f0132010-02-19 11:23:00 -0800846 * alerting the user that they need to enter a password at some point.
847 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700848 * <p>In that case, you may need to wait until the user responds, which
849 * could take hours or days or forever. When the user does respond and
850 * supply a new password, the account manager will broadcast the
851 * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
852 * use to try again.
Dan Egnor661f0132010-02-19 11:23:00 -0800853 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700854 * <p>If notifyAuthFailure is not set, it is the application's
855 * responsibility to launch the returned Intent at some point.
856 * Either way, the result from this call will not wait for user action.
Dan Egnor661f0132010-02-19 11:23:00 -0800857 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700858 * <p>Some authenticators have auth token <em>types</em>, whose value
859 * is authenticator-dependent. Some services use different token types to
860 * access different functionality -- for example, Google uses different auth
861 * tokens to access Gmail and Google Calendar for the same account.
Dan Egnor661f0132010-02-19 11:23:00 -0800862 *
863 * <p>This method may be called from any thread, but the returned
864 * {@link AccountManagerFuture} must not be used on the main thread.
865 *
866 * <p>This method requires the caller to hold the permission
867 * {@link android.Manifest.permission#USE_CREDENTIALS}.
868 *
869 * @param account The account to fetch an auth token for
870 * @param authTokenType The auth token type, an authenticator-dependent
871 * string token, must not be null
Dan Egnor661f0132010-02-19 11:23:00 -0800872 * @param notifyAuthFailure True to add a notification to prompt the
873 * user for a password if necessary, false to leave that to the caller
874 * @param callback Callback to invoke when the request completes,
875 * null for no callback
876 * @param handler {@link Handler} identifying the callback thread,
877 * null for the main thread
878 * @return An {@link AccountManagerFuture} which resolves to a Bundle with
879 * at least the following fields on success:
880 * <ul>
881 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
882 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
883 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
884 * </ul>
885 *
886 * (Other authenticator-specific values may be returned.) If the user
887 * must enter credentials, the returned Bundle contains only
888 * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
889 *
Dan Egnor8e4378b2010-08-02 18:22:09 -0700890 * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
Dan Egnor661f0132010-02-19 11:23:00 -0800891 * <ul>
892 * <li> {@link AuthenticatorException} if the authenticator failed to respond
893 * <li> {@link OperationCanceledException} if the operation is canceled for
894 * any reason, incluidng the user canceling a credential request
895 * <li> {@link IOException} if the authenticator experienced an I/O problem
896 * creating a new auth token, usually because of network trouble
897 * </ul>
Dan Egnor8e4378b2010-08-02 18:22:09 -0700898 * If the account is no longer present on the device, the return value is
899 * authenticator-dependent. The caller should verify the validity of the
900 * account before requesting an auth token.
Fred Quintanaad93a322011-09-08 13:21:01 -0700901 * @deprecated use {@link #getAuthToken(Account, String, android.os.Bundle,
902 * boolean, AccountManagerCallback, android.os.Handler)} instead
Dan Egnor661f0132010-02-19 11:23:00 -0800903 */
Fred Quintanaad93a322011-09-08 13:21:01 -0700904 @Deprecated
Dan Egnor661f0132010-02-19 11:23:00 -0800905 public AccountManagerFuture<Bundle> getAuthToken(
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700906 final Account account, final String authTokenType,
907 final boolean notifyAuthFailure,
Dan Egnor661f0132010-02-19 11:23:00 -0800908 AccountManagerCallback<Bundle> callback, Handler handler) {
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700909 return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback,
910 handler);
Dan Egnor661f0132010-02-19 11:23:00 -0800911 }
912
913 /**
Fred Quintanaad93a322011-09-08 13:21:01 -0700914 * Gets an auth token of the specified type for a particular account,
915 * optionally raising a notification if the user must enter credentials.
916 * This method is intended for background tasks and services where the
917 * user should not be immediately interrupted with a password prompt.
918 *
919 * <p>If a previously generated auth token is cached for this account and
920 * type, then it is returned. Otherwise, if a saved password is
921 * available, it is sent to the server to generate a new auth token.
922 * Otherwise, an {@link Intent} is returned which, when started, will
923 * prompt the user for a password. If the notifyAuthFailure parameter is
924 * set, a status bar notification is also created with the same Intent,
925 * alerting the user that they need to enter a password at some point.
926 *
927 * <p>In that case, you may need to wait until the user responds, which
928 * could take hours or days or forever. When the user does respond and
929 * supply a new password, the account manager will broadcast the
930 * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
931 * use to try again.
932 *
933 * <p>If notifyAuthFailure is not set, it is the application's
934 * responsibility to launch the returned Intent at some point.
935 * Either way, the result from this call will not wait for user action.
936 *
937 * <p>Some authenticators have auth token <em>types</em>, whose value
938 * is authenticator-dependent. Some services use different token types to
939 * access different functionality -- for example, Google uses different auth
940 * tokens to access Gmail and Google Calendar for the same account.
941 *
942 * <p>This method may be called from any thread, but the returned
943 * {@link AccountManagerFuture} must not be used on the main thread.
944 *
945 * <p>This method requires the caller to hold the permission
946 * {@link android.Manifest.permission#USE_CREDENTIALS}.
947 *
948 * @param account The account to fetch an auth token for
949 * @param authTokenType The auth token type, an authenticator-dependent
950 * string token, must not be null
951 * @param options Authenticator-specific options for the request,
952 * may be null or empty
953 * @param notifyAuthFailure True to add a notification to prompt the
954 * user for a password if necessary, false to leave that to the caller
955 * @param callback Callback to invoke when the request completes,
956 * null for no callback
957 * @param handler {@link Handler} identifying the callback thread,
958 * null for the main thread
959 * @return An {@link AccountManagerFuture} which resolves to a Bundle with
960 * at least the following fields on success:
961 * <ul>
962 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
963 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
964 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
965 * </ul>
966 *
967 * (Other authenticator-specific values may be returned.) If the user
968 * must enter credentials, the returned Bundle contains only
969 * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
970 *
971 * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
972 * <ul>
973 * <li> {@link AuthenticatorException} if the authenticator failed to respond
974 * <li> {@link OperationCanceledException} if the operation is canceled for
975 * any reason, incluidng the user canceling a credential request
976 * <li> {@link IOException} if the authenticator experienced an I/O problem
977 * creating a new auth token, usually because of network trouble
978 * </ul>
979 * If the account is no longer present on the device, the return value is
980 * authenticator-dependent. The caller should verify the validity of the
981 * account before requesting an auth token.
982 */
983 public AccountManagerFuture<Bundle> getAuthToken(
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700984 final Account account, final String authTokenType, final Bundle options,
985 final boolean notifyAuthFailure,
Fred Quintanaad93a322011-09-08 13:21:01 -0700986 AccountManagerCallback<Bundle> callback, Handler handler) {
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700987
Fred Quintanaad93a322011-09-08 13:21:01 -0700988 if (account == null) throw new IllegalArgumentException("account is null");
989 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Costin Manolachee5847ad2011-09-14 12:52:19 -0700990 final Bundle optionsIn = new Bundle();
991 if (options != null) {
992 optionsIn.putAll(options);
993 }
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700994 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
Fred Quintanaad93a322011-09-08 13:21:01 -0700995 return new AmsTask(null, handler, callback) {
996 public void doWork() throws RemoteException {
997 mService.getAuthToken(mResponse, account, authTokenType,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700998 notifyAuthFailure, false /* expectActivityLaunch */, optionsIn);
Fred Quintanaad93a322011-09-08 13:21:01 -0700999 }
1000 }.start();
1001 }
1002
1003 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001004 * Asks the user to add an account of a specified type. The authenticator
1005 * for this account type processes this request with the appropriate user
1006 * interface. If the user does elect to create a new account, the account
1007 * name is returned.
1008 *
1009 * <p>This method may be called from any thread, but the returned
1010 * {@link AccountManagerFuture} must not be used on the main thread.
1011 *
1012 * <p>This method requires the caller to hold the permission
1013 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1014 *
1015 * @param accountType The type of account to add; must not be null
1016 * @param authTokenType The type of auth token (see {@link #getAuthToken})
1017 * this account will need to be able to generate, null for none
1018 * @param requiredFeatures The features (see {@link #hasFeatures}) this
1019 * account must have, null for none
1020 * @param addAccountOptions Authenticator-specific options for the request,
1021 * may be null or empty
1022 * @param activity The {@link Activity} context to use for launching a new
1023 * authenticator-defined sub-Activity to prompt the user to create an
1024 * account; used only to call startActivity(); if null, the prompt
1025 * will not be launched directly, but the necessary {@link Intent}
1026 * will be returned to the caller instead
1027 * @param callback Callback to invoke when the request completes,
1028 * null for no callback
1029 * @param handler {@link Handler} identifying the callback thread,
1030 * null for the main thread
Doug Zongkerff592dc2010-02-23 12:26:33 -08001031 * @return An {@link AccountManagerFuture} which resolves to a Bundle with
Dan Egnor661f0132010-02-19 11:23:00 -08001032 * these fields if activity was specified and an account was created:
1033 * <ul>
1034 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
1035 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
1036 * </ul>
1037 *
1038 * If no activity was specified, the returned Bundle contains only
1039 * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
Dan Egnor8e4378b2010-08-02 18:22:09 -07001040 * actual account creation process. If an error occurred,
1041 * {@link AccountManagerFuture#getResult()} throws:
Dan Egnor661f0132010-02-19 11:23:00 -08001042 * <ul>
1043 * <li> {@link AuthenticatorException} if no authenticator was registered for
1044 * this account type or the authenticator failed to respond
1045 * <li> {@link OperationCanceledException} if the operation was canceled for
1046 * any reason, including the user canceling the creation process
1047 * <li> {@link IOException} if the authenticator experienced an I/O problem
1048 * creating a new account, usually because of network trouble
1049 * </ul>
1050 */
1051 public AccountManagerFuture<Bundle> addAccount(final String accountType,
1052 final String authTokenType, final String[] requiredFeatures,
1053 final Bundle addAccountOptions,
1054 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -07001055 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolachee5847ad2011-09-14 12:52:19 -07001056 final Bundle optionsIn = new Bundle();
1057 if (addAccountOptions != null) {
1058 optionsIn.putAll(addAccountOptions);
1059 }
1060 optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001061
Dan Egnor661f0132010-02-19 11:23:00 -08001062 return new AmsTask(activity, handler, callback) {
1063 public void doWork() throws RemoteException {
Dan Egnor661f0132010-02-19 11:23:00 -08001064 mService.addAcount(mResponse, accountType, authTokenType,
Costin Manolachee5847ad2011-09-14 12:52:19 -07001065 requiredFeatures, activity != null, optionsIn);
Dan Egnor661f0132010-02-19 11:23:00 -08001066 }
1067 }.start();
1068 }
1069
1070 /**
1071 * Confirms that the user knows the password for an account to make extra
1072 * sure they are the owner of the account. The user-entered password can
1073 * be supplied directly, otherwise the authenticator for this account type
1074 * prompts the user with the appropriate interface. This method is
1075 * intended for applications which want extra assurance; for example, the
1076 * phone lock screen uses this to let the user unlock the phone with an
1077 * account password if they forget the lock pattern.
1078 *
1079 * <p>If the user-entered password matches a saved password for this
1080 * account, the request is considered valid; otherwise the authenticator
1081 * verifies the password (usually by contacting the server).
1082 *
1083 * <p>This method may be called from any thread, but the returned
1084 * {@link AccountManagerFuture} must not be used on the main thread.
1085 *
1086 * <p>This method requires the caller to hold the permission
1087 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1088 *
1089 * @param account The account to confirm password knowledge for
1090 * @param options Authenticator-specific options for the request;
1091 * if the {@link #KEY_PASSWORD} string field is present, the
1092 * authenticator may use it directly rather than prompting the user;
1093 * may be null or empty
1094 * @param activity The {@link Activity} context to use for launching a new
1095 * authenticator-defined sub-Activity to prompt the user to enter a
1096 * password; used only to call startActivity(); if null, the prompt
1097 * will not be launched directly, but the necessary {@link Intent}
1098 * will be returned to the caller instead
1099 * @param callback Callback to invoke when the request completes,
1100 * null for no callback
1101 * @param handler {@link Handler} identifying the callback thread,
1102 * null for the main thread
1103 * @return An {@link AccountManagerFuture} which resolves to a Bundle
1104 * with these fields if activity or password was supplied and
1105 * the account was successfully verified:
1106 * <ul>
1107 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
1108 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
1109 * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
1110 * </ul>
1111 *
1112 * If no activity or password was specified, the returned Bundle contains
1113 * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
Dan Egnor8e4378b2010-08-02 18:22:09 -07001114 * password prompt. If an error occurred,
1115 * {@link AccountManagerFuture#getResult()} throws:
Dan Egnor661f0132010-02-19 11:23:00 -08001116 * <ul>
1117 * <li> {@link AuthenticatorException} if the authenticator failed to respond
1118 * <li> {@link OperationCanceledException} if the operation was canceled for
1119 * any reason, including the user canceling the password prompt
1120 * <li> {@link IOException} if the authenticator experienced an I/O problem
1121 * verifying the password, usually because of network trouble
1122 * </ul>
Fred Quintana756b7352009-10-21 13:43:10 -07001123 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001124 public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
1125 final Bundle options,
1126 final Activity activity,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001127 final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -07001128 final Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -07001129 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanaa698f422009-04-08 19:14:54 -07001130 return new AmsTask(activity, handler, callback) {
1131 public void doWork() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001132 mService.confirmCredentials(mResponse, account, options, activity != null);
Fred Quintanaa698f422009-04-08 19:14:54 -07001133 }
Fred Quintana33269202009-04-20 16:05:10 -07001134 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -07001135 }
1136
Fred Quintana756b7352009-10-21 13:43:10 -07001137 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001138 * Asks the user to enter a new password for an account, updating the
1139 * saved credentials for the account. Normally this happens automatically
1140 * when the server rejects credentials during an auth token fetch, but this
1141 * can be invoked directly to ensure we have the correct credentials stored.
Fred Quintana756b7352009-10-21 13:43:10 -07001142 *
Dan Egnor661f0132010-02-19 11:23:00 -08001143 * <p>This method may be called from any thread, but the returned
1144 * {@link AccountManagerFuture} must not be used on the main thread.
1145 *
1146 * <p>This method requires the caller to hold the permission
1147 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1148 *
1149 * @param account The account to update credentials for
1150 * @param authTokenType The credentials entered must allow an auth token
1151 * of this type to be created (but no actual auth token is returned);
1152 * may be null
1153 * @param options Authenticator-specific options for the request;
1154 * may be null or empty
1155 * @param activity The {@link Activity} context to use for launching a new
1156 * authenticator-defined sub-Activity to prompt the user to enter a
1157 * password; used only to call startActivity(); if null, the prompt
1158 * will not be launched directly, but the necessary {@link Intent}
1159 * will be returned to the caller instead
1160 * @param callback Callback to invoke when the request completes,
1161 * null for no callback
1162 * @param handler {@link Handler} identifying the callback thread,
1163 * null for the main thread
1164 * @return An {@link AccountManagerFuture} which resolves to a Bundle
1165 * with these fields if an activity was supplied and the account
1166 * credentials were successfully updated:
Fred Quintana756b7352009-10-21 13:43:10 -07001167 * <ul>
Dan Egnor661f0132010-02-19 11:23:00 -08001168 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
1169 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
Fred Quintana756b7352009-10-21 13:43:10 -07001170 * </ul>
Dan Egnor661f0132010-02-19 11:23:00 -08001171 *
1172 * If no activity was specified, the returned Bundle contains only
1173 * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
Dan Egnor8e4378b2010-08-02 18:22:09 -07001174 * password prompt. If an error occurred,
1175 * {@link AccountManagerFuture#getResult()} throws:
Dan Egnor661f0132010-02-19 11:23:00 -08001176 * <ul>
1177 * <li> {@link AuthenticatorException} if the authenticator failed to respond
1178 * <li> {@link OperationCanceledException} if the operation was canceled for
1179 * any reason, including the user canceling the password prompt
1180 * <li> {@link IOException} if the authenticator experienced an I/O problem
1181 * verifying the password, usually because of network trouble
1182 * </ul>
Fred Quintana756b7352009-10-21 13:43:10 -07001183 */
1184 public AccountManagerFuture<Bundle> updateCredentials(final Account account,
1185 final String authTokenType,
Fred Quintana31957f12009-10-21 13:43:10 -07001186 final Bundle options, final Activity activity,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001187 final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -07001188 final Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -07001189 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanaa698f422009-04-08 19:14:54 -07001190 return new AmsTask(activity, handler, callback) {
1191 public void doWork() throws RemoteException {
1192 mService.updateCredentials(mResponse, account, authTokenType, activity != null,
Fred Quintana31957f12009-10-21 13:43:10 -07001193 options);
Fred Quintanaa698f422009-04-08 19:14:54 -07001194 }
Fred Quintana33269202009-04-20 16:05:10 -07001195 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -07001196 }
1197
Fred Quintana756b7352009-10-21 13:43:10 -07001198 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001199 * Offers the user an opportunity to change an authenticator's settings.
1200 * These properties are for the authenticator in general, not a particular
1201 * account. Not all authenticators support this method.
Fred Quintana756b7352009-10-21 13:43:10 -07001202 *
Dan Egnor661f0132010-02-19 11:23:00 -08001203 * <p>This method may be called from any thread, but the returned
1204 * {@link AccountManagerFuture} must not be used on the main thread.
1205 *
1206 * <p>This method requires the caller to hold the permission
1207 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1208 *
1209 * @param accountType The account type associated with the authenticator
1210 * to adjust
1211 * @param activity The {@link Activity} context to use for launching a new
1212 * authenticator-defined sub-Activity to adjust authenticator settings;
1213 * used only to call startActivity(); if null, the settings dialog will
1214 * not be launched directly, but the necessary {@link Intent} will be
1215 * returned to the caller instead
1216 * @param callback Callback to invoke when the request completes,
1217 * null for no callback
1218 * @param handler {@link Handler} identifying the callback thread,
1219 * null for the main thread
1220 * @return An {@link AccountManagerFuture} which resolves to a Bundle
1221 * which is empty if properties were edited successfully, or
1222 * if no activity was specified, contains only {@link #KEY_INTENT}
1223 * needed to launch the authenticator's settings dialog.
Dan Egnor8e4378b2010-08-02 18:22:09 -07001224 * If an error occurred, {@link AccountManagerFuture#getResult()}
1225 * throws:
Fred Quintana756b7352009-10-21 13:43:10 -07001226 * <ul>
Dan Egnor661f0132010-02-19 11:23:00 -08001227 * <li> {@link AuthenticatorException} if no authenticator was registered for
1228 * this account type or the authenticator failed to respond
1229 * <li> {@link OperationCanceledException} if the operation was canceled for
1230 * any reason, including the user canceling the settings dialog
1231 * <li> {@link IOException} if the authenticator experienced an I/O problem
1232 * updating settings, usually because of network trouble
Fred Quintana756b7352009-10-21 13:43:10 -07001233 * </ul>
Fred Quintana756b7352009-10-21 13:43:10 -07001234 */
1235 public AccountManagerFuture<Bundle> editProperties(final String accountType,
1236 final Activity activity, final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -07001237 final Handler handler) {
Fred Quintana382601f2010-03-25 12:25:10 -07001238 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanaa698f422009-04-08 19:14:54 -07001239 return new AmsTask(activity, handler, callback) {
1240 public void doWork() throws RemoteException {
1241 mService.editProperties(mResponse, accountType, activity != null);
1242 }
Fred Quintana33269202009-04-20 16:05:10 -07001243 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -07001244 }
1245
1246 private void ensureNotOnMainThread() {
1247 final Looper looper = Looper.myLooper();
1248 if (looper != null && looper == mContext.getMainLooper()) {
Fred Quintana53bd2522010-02-05 15:28:12 -08001249 final IllegalStateException exception = new IllegalStateException(
1250 "calling this from your main thread can lead to deadlock");
1251 Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
1252 exception);
Fred Quintana751fdc02010-02-09 14:13:18 -08001253 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) {
1254 throw exception;
1255 }
Fred Quintana60307342009-03-24 22:48:12 -07001256 }
1257 }
1258
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001259 private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback,
1260 final AccountManagerFuture<Bundle> future) {
Fred Quintanad9d2f112009-04-23 13:36:27 -07001261 handler = handler == null ? mMainHandler : handler;
1262 handler.post(new Runnable() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001263 public void run() {
1264 callback.run(future);
1265 }
1266 });
1267 }
Fred Quintana60307342009-03-24 22:48:12 -07001268
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001269 private void postToHandler(Handler handler, final OnAccountsUpdateListener listener,
Fred Quintanad9d2f112009-04-23 13:36:27 -07001270 final Account[] accounts) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001271 final Account[] accountsCopy = new Account[accounts.length];
1272 // send a copy to make sure that one doesn't
1273 // change what another sees
1274 System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length);
1275 handler = (handler == null) ? mMainHandler : handler;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001276 handler.post(new Runnable() {
1277 public void run() {
Costin Manolacheb6437242009-09-10 16:14:12 -07001278 try {
1279 listener.onAccountsUpdated(accountsCopy);
1280 } catch (SQLException e) {
1281 // Better luck next time. If the problem was disk-full,
1282 // the STORAGE_OK intent will re-trigger the update.
1283 Log.e(TAG, "Can't update accounts", e);
1284 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001285 }
1286 });
1287 }
1288
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001289 private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
Fred Quintanaa698f422009-04-08 19:14:54 -07001290 final IAccountManagerResponse mResponse;
1291 final Handler mHandler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001292 final AccountManagerCallback<Bundle> mCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -07001293 final Activity mActivity;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001294 public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001295 super(new Callable<Bundle>() {
1296 public Bundle call() throws Exception {
1297 throw new IllegalStateException("this should never be called");
1298 }
1299 });
1300
1301 mHandler = handler;
1302 mCallback = callback;
1303 mActivity = activity;
1304 mResponse = new Response();
Fred Quintana33269202009-04-20 16:05:10 -07001305 }
1306
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001307 public final AccountManagerFuture<Bundle> start() {
1308 try {
1309 doWork();
1310 } catch (RemoteException e) {
1311 setException(e);
1312 }
Fred Quintana33269202009-04-20 16:05:10 -07001313 return this;
Fred Quintana60307342009-03-24 22:48:12 -07001314 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001315
Fred Quintana96580e02010-03-04 13:42:42 -08001316 protected void set(Bundle bundle) {
1317 // TODO: somehow a null is being set as the result of the Future. Log this
1318 // case to help debug where this is occurring. When this bug is fixed this
1319 // condition statement should be removed.
1320 if (bundle == null) {
1321 Log.e(TAG, "the bundle must not be null", new Exception());
1322 }
1323 super.set(bundle);
1324 }
1325
Fred Quintanaa698f422009-04-08 19:14:54 -07001326 public abstract void doWork() throws RemoteException;
1327
1328 private Bundle internalGetResult(Long timeout, TimeUnit unit)
1329 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintana53bd2522010-02-05 15:28:12 -08001330 if (!isDone()) {
1331 ensureNotOnMainThread();
1332 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001333 try {
1334 if (timeout == null) {
1335 return get();
1336 } else {
1337 return get(timeout, unit);
1338 }
1339 } catch (CancellationException e) {
1340 throw new OperationCanceledException();
1341 } catch (TimeoutException e) {
1342 // fall through and cancel
1343 } catch (InterruptedException e) {
1344 // fall through and cancel
1345 } catch (ExecutionException e) {
1346 final Throwable cause = e.getCause();
1347 if (cause instanceof IOException) {
1348 throw (IOException) cause;
1349 } else if (cause instanceof UnsupportedOperationException) {
1350 throw new AuthenticatorException(cause);
1351 } else if (cause instanceof AuthenticatorException) {
1352 throw (AuthenticatorException) cause;
1353 } else if (cause instanceof RuntimeException) {
1354 throw (RuntimeException) cause;
1355 } else if (cause instanceof Error) {
1356 throw (Error) cause;
1357 } else {
1358 throw new IllegalStateException(cause);
1359 }
1360 } finally {
1361 cancel(true /* interrupt if running */);
1362 }
1363 throw new OperationCanceledException();
1364 }
1365
1366 public Bundle getResult()
1367 throws OperationCanceledException, IOException, AuthenticatorException {
1368 return internalGetResult(null, null);
1369 }
1370
1371 public Bundle getResult(long timeout, TimeUnit unit)
1372 throws OperationCanceledException, IOException, AuthenticatorException {
1373 return internalGetResult(timeout, unit);
1374 }
1375
1376 protected void done() {
1377 if (mCallback != null) {
1378 postToHandler(mHandler, mCallback, this);
1379 }
1380 }
1381
1382 /** Handles the responses from the AccountManager */
1383 private class Response extends IAccountManagerResponse.Stub {
1384 public void onResult(Bundle bundle) {
Brian Carlstrom46703b02011-04-06 15:41:29 -07001385 Intent intent = bundle.getParcelable(KEY_INTENT);
Fred Quintanaa698f422009-04-08 19:14:54 -07001386 if (intent != null && mActivity != null) {
1387 // since the user provided an Activity we will silently start intents
1388 // that we see
1389 mActivity.startActivity(intent);
1390 // leave the Future running to wait for the real response to this request
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001391 } else if (bundle.getBoolean("retry")) {
1392 try {
1393 doWork();
1394 } catch (RemoteException e) {
1395 // this will only happen if the system process is dead, which means
1396 // we will be dying ourselves
1397 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001398 } else {
1399 set(bundle);
1400 }
1401 }
1402
1403 public void onError(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001404 if (code == ERROR_CODE_CANCELED) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001405 // the authenticator indicated that this request was canceled, do so now
1406 cancel(true /* mayInterruptIfRunning */);
1407 return;
1408 }
1409 setException(convertErrorToException(code, message));
1410 }
1411 }
1412
Fred Quintana60307342009-03-24 22:48:12 -07001413 }
1414
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001415 private abstract class BaseFutureTask<T> extends FutureTask<T> {
1416 final public IAccountManagerResponse mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07001417 final Handler mHandler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001418
1419 public BaseFutureTask(Handler handler) {
1420 super(new Callable<T>() {
1421 public T call() throws Exception {
Fred Quintanaa698f422009-04-08 19:14:54 -07001422 throw new IllegalStateException("this should never be called");
1423 }
1424 });
Fred Quintanaa698f422009-04-08 19:14:54 -07001425 mHandler = handler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001426 mResponse = new Response();
Fred Quintana60307342009-03-24 22:48:12 -07001427 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001428
1429 public abstract void doWork() throws RemoteException;
1430
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001431 public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException;
Fred Quintanaa698f422009-04-08 19:14:54 -07001432
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001433 protected void postRunnableToHandler(Runnable runnable) {
1434 Handler handler = (mHandler == null) ? mMainHandler : mHandler;
1435 handler.post(runnable);
Fred Quintanaa698f422009-04-08 19:14:54 -07001436 }
1437
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001438 protected void startTask() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001439 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001440 doWork();
1441 } catch (RemoteException e) {
1442 setException(e);
Fred Quintanaa698f422009-04-08 19:14:54 -07001443 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001444 }
1445
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001446 protected class Response extends IAccountManagerResponse.Stub {
Fred Quintanaa698f422009-04-08 19:14:54 -07001447 public void onResult(Bundle bundle) {
1448 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001449 T result = bundleToResult(bundle);
1450 if (result == null) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001451 return;
1452 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001453 set(result);
1454 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001455 } catch (ClassCastException e) {
1456 // we will set the exception below
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001457 } catch (AuthenticatorException e) {
1458 // we will set the exception below
Fred Quintanaa698f422009-04-08 19:14:54 -07001459 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001460 onError(ERROR_CODE_INVALID_RESPONSE, "no result in response");
Fred Quintanaa698f422009-04-08 19:14:54 -07001461 }
1462
1463 public void onError(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001464 if (code == ERROR_CODE_CANCELED) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001465 cancel(true /* mayInterruptIfRunning */);
1466 return;
1467 }
1468 setException(convertErrorToException(code, message));
1469 }
1470 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001471 }
1472
1473 private abstract class Future2Task<T>
1474 extends BaseFutureTask<T> implements AccountManagerFuture<T> {
1475 final AccountManagerCallback<T> mCallback;
1476 public Future2Task(Handler handler, AccountManagerCallback<T> callback) {
1477 super(handler);
1478 mCallback = callback;
1479 }
1480
1481 protected void done() {
1482 if (mCallback != null) {
1483 postRunnableToHandler(new Runnable() {
1484 public void run() {
1485 mCallback.run(Future2Task.this);
1486 }
1487 });
1488 }
1489 }
1490
1491 public Future2Task<T> start() {
1492 startTask();
1493 return this;
1494 }
1495
1496 private T internalGetResult(Long timeout, TimeUnit unit)
1497 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintana53bd2522010-02-05 15:28:12 -08001498 if (!isDone()) {
1499 ensureNotOnMainThread();
1500 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001501 try {
1502 if (timeout == null) {
1503 return get();
1504 } else {
1505 return get(timeout, unit);
1506 }
1507 } catch (InterruptedException e) {
1508 // fall through and cancel
1509 } catch (TimeoutException e) {
1510 // fall through and cancel
1511 } catch (CancellationException e) {
1512 // fall through and cancel
1513 } catch (ExecutionException e) {
1514 final Throwable cause = e.getCause();
1515 if (cause instanceof IOException) {
1516 throw (IOException) cause;
1517 } else if (cause instanceof UnsupportedOperationException) {
1518 throw new AuthenticatorException(cause);
1519 } else if (cause instanceof AuthenticatorException) {
1520 throw (AuthenticatorException) cause;
1521 } else if (cause instanceof RuntimeException) {
1522 throw (RuntimeException) cause;
1523 } else if (cause instanceof Error) {
1524 throw (Error) cause;
1525 } else {
1526 throw new IllegalStateException(cause);
1527 }
1528 } finally {
1529 cancel(true /* interrupt if running */);
1530 }
1531 throw new OperationCanceledException();
1532 }
1533
1534 public T getResult()
1535 throws OperationCanceledException, IOException, AuthenticatorException {
1536 return internalGetResult(null, null);
1537 }
1538
1539 public T getResult(long timeout, TimeUnit unit)
1540 throws OperationCanceledException, IOException, AuthenticatorException {
1541 return internalGetResult(timeout, unit);
1542 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001543
Fred Quintana60307342009-03-24 22:48:12 -07001544 }
1545
Fred Quintanaa698f422009-04-08 19:14:54 -07001546 private Exception convertErrorToException(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001547 if (code == ERROR_CODE_NETWORK_ERROR) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001548 return new IOException(message);
Fred Quintana60307342009-03-24 22:48:12 -07001549 }
Fred Quintana60307342009-03-24 22:48:12 -07001550
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001551 if (code == ERROR_CODE_UNSUPPORTED_OPERATION) {
Fred Quintana33269202009-04-20 16:05:10 -07001552 return new UnsupportedOperationException(message);
Fred Quintana60307342009-03-24 22:48:12 -07001553 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001554
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001555 if (code == ERROR_CODE_INVALID_RESPONSE) {
Fred Quintana33269202009-04-20 16:05:10 -07001556 return new AuthenticatorException(message);
Fred Quintanaa698f422009-04-08 19:14:54 -07001557 }
1558
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001559 if (code == ERROR_CODE_BAD_ARGUMENTS) {
Fred Quintana33269202009-04-20 16:05:10 -07001560 return new IllegalArgumentException(message);
1561 }
1562
1563 return new AuthenticatorException(message);
1564 }
1565
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001566 private class GetAuthTokenByTypeAndFeaturesTask
1567 extends AmsTask implements AccountManagerCallback<Bundle> {
Fred Quintana33269202009-04-20 16:05:10 -07001568 GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
1569 final String[] features, Activity activityForPrompting,
1570 final Bundle addAccountOptions, final Bundle loginOptions,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001571 AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintana33269202009-04-20 16:05:10 -07001572 super(activityForPrompting, handler, callback);
1573 if (accountType == null) throw new IllegalArgumentException("account type is null");
1574 mAccountType = accountType;
1575 mAuthTokenType = authTokenType;
1576 mFeatures = features;
1577 mAddAccountOptions = addAccountOptions;
1578 mLoginOptions = loginOptions;
1579 mMyCallback = this;
1580 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001581 volatile AccountManagerFuture<Bundle> mFuture = null;
Fred Quintana33269202009-04-20 16:05:10 -07001582 final String mAccountType;
1583 final String mAuthTokenType;
1584 final String[] mFeatures;
1585 final Bundle mAddAccountOptions;
1586 final Bundle mLoginOptions;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001587 final AccountManagerCallback<Bundle> mMyCallback;
Fred Quintanaf0fd8432010-03-08 12:48:05 -08001588 private volatile int mNumAccounts = 0;
Fred Quintana33269202009-04-20 16:05:10 -07001589
1590 public void doWork() throws RemoteException {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001591 getAccountsByTypeAndFeatures(mAccountType, mFeatures,
1592 new AccountManagerCallback<Account[]>() {
1593 public void run(AccountManagerFuture<Account[]> future) {
1594 Account[] accounts;
Fred Quintana33269202009-04-20 16:05:10 -07001595 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001596 accounts = future.getResult();
1597 } catch (OperationCanceledException e) {
1598 setException(e);
1599 return;
1600 } catch (IOException e) {
1601 setException(e);
1602 return;
1603 } catch (AuthenticatorException e) {
1604 setException(e);
1605 return;
Fred Quintana33269202009-04-20 16:05:10 -07001606 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001607
Fred Quintanaf0fd8432010-03-08 12:48:05 -08001608 mNumAccounts = accounts.length;
1609
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001610 if (accounts.length == 0) {
1611 if (mActivity != null) {
1612 // no accounts, add one now. pretend that the user directly
1613 // made this request
1614 mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
1615 mAddAccountOptions, mActivity, mMyCallback, mHandler);
1616 } else {
1617 // send result since we can't prompt to add an account
1618 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001619 result.putString(KEY_ACCOUNT_NAME, null);
1620 result.putString(KEY_ACCOUNT_TYPE, null);
1621 result.putString(KEY_AUTHTOKEN, null);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001622 try {
1623 mResponse.onResult(result);
1624 } catch (RemoteException e) {
1625 // this will never happen
1626 }
1627 // we are done
1628 }
1629 } else if (accounts.length == 1) {
1630 // have a single account, return an authtoken for it
1631 if (mActivity == null) {
1632 mFuture = getAuthToken(accounts[0], mAuthTokenType,
1633 false /* notifyAuthFailure */, mMyCallback, mHandler);
1634 } else {
1635 mFuture = getAuthToken(accounts[0],
1636 mAuthTokenType, mLoginOptions,
Fred Quintana33269202009-04-20 16:05:10 -07001637 mActivity, mMyCallback, mHandler);
1638 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001639 } else {
1640 if (mActivity != null) {
1641 IAccountManagerResponse chooseResponse =
1642 new IAccountManagerResponse.Stub() {
1643 public void onResult(Bundle value) throws RemoteException {
1644 Account account = new Account(
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001645 value.getString(KEY_ACCOUNT_NAME),
1646 value.getString(KEY_ACCOUNT_TYPE));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001647 mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
1648 mActivity, mMyCallback, mHandler);
1649 }
Fred Quintana33269202009-04-20 16:05:10 -07001650
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001651 public void onError(int errorCode, String errorMessage)
1652 throws RemoteException {
1653 mResponse.onError(errorCode, errorMessage);
1654 }
1655 };
1656 // have many accounts, launch the chooser
1657 Intent intent = new Intent();
1658 intent.setClassName("android",
1659 "android.accounts.ChooseAccountActivity");
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001660 intent.putExtra(KEY_ACCOUNTS, accounts);
1661 intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001662 new AccountManagerResponse(chooseResponse));
1663 mActivity.startActivity(intent);
1664 // the result will arrive via the IAccountManagerResponse
1665 } else {
1666 // send result since we can't prompt to select an account
1667 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001668 result.putString(KEY_ACCOUNTS, null);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001669 try {
1670 mResponse.onResult(result);
1671 } catch (RemoteException e) {
1672 // this will never happen
1673 }
1674 // we are done
Fred Quintana33269202009-04-20 16:05:10 -07001675 }
Fred Quintana33269202009-04-20 16:05:10 -07001676 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001677 }}, mHandler);
Fred Quintana33269202009-04-20 16:05:10 -07001678 }
1679
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001680 public void run(AccountManagerFuture<Bundle> future) {
Fred Quintana33269202009-04-20 16:05:10 -07001681 try {
Fred Quintanaf0fd8432010-03-08 12:48:05 -08001682 final Bundle result = future.getResult();
1683 if (mNumAccounts == 0) {
1684 final String accountName = result.getString(KEY_ACCOUNT_NAME);
1685 final String accountType = result.getString(KEY_ACCOUNT_TYPE);
1686 if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
1687 setException(new AuthenticatorException("account not in result"));
1688 return;
1689 }
1690 final Account account = new Account(accountName, accountType);
1691 mNumAccounts = 1;
1692 getAuthToken(account, mAuthTokenType, null /* options */, mActivity,
1693 mMyCallback, mHandler);
1694 return;
1695 }
1696 set(result);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001697 } catch (OperationCanceledException e) {
1698 cancel(true /* mayInterruptIfRUnning */);
1699 } catch (IOException e) {
1700 setException(e);
1701 } catch (AuthenticatorException e) {
1702 setException(e);
Fred Quintana33269202009-04-20 16:05:10 -07001703 }
1704 }
1705 }
1706
Fred Quintana756b7352009-10-21 13:43:10 -07001707 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001708 * This convenience helper combines the functionality of
1709 * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and
1710 * {@link #addAccount}.
Fred Quintana756b7352009-10-21 13:43:10 -07001711 *
Dan Egnor661f0132010-02-19 11:23:00 -08001712 * <p>This method gets a list of the accounts matching the
1713 * specified type and feature set; if there is exactly one, it is
1714 * used; if there are more than one, the user is prompted to pick one;
1715 * if there are none, the user is prompted to add one. Finally,
1716 * an auth token is acquired for the chosen account.
1717 *
1718 * <p>This method may be called from any thread, but the returned
1719 * {@link AccountManagerFuture} must not be used on the main thread.
1720 *
1721 * <p>This method requires the caller to hold the permission
1722 * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1723 *
1724 * @param accountType The account type required
Doug Zongkerff592dc2010-02-23 12:26:33 -08001725 * (see {@link #getAccountsByType}), must not be null
Dan Egnor661f0132010-02-19 11:23:00 -08001726 * @param authTokenType The desired auth token type
1727 * (see {@link #getAuthToken}), must not be null
1728 * @param features Required features for the account
1729 * (see {@link #getAccountsByTypeAndFeatures}), may be null or empty
1730 * @param activity The {@link Activity} context to use for launching new
1731 * sub-Activities to prompt to add an account, select an account,
1732 * and/or enter a password, as necessary; used only to call
1733 * startActivity(); should not be null
1734 * @param addAccountOptions Authenticator-specific options to use for
1735 * adding new accounts; may be null or empty
1736 * @param getAuthTokenOptions Authenticator-specific options to use for
1737 * getting auth tokens; may be null or empty
1738 * @param callback Callback to invoke when the request completes,
1739 * null for no callback
1740 * @param handler {@link Handler} identifying the callback thread,
1741 * null for the main thread
1742 * @return An {@link AccountManagerFuture} which resolves to a Bundle with
1743 * at least the following fields:
Fred Quintana756b7352009-10-21 13:43:10 -07001744 * <ul>
Dan Egnor661f0132010-02-19 11:23:00 -08001745 * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account
1746 * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
1747 * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
Fred Quintana756b7352009-10-21 13:43:10 -07001748 * </ul>
Dan Egnor661f0132010-02-19 11:23:00 -08001749 *
Dan Egnor8e4378b2010-08-02 18:22:09 -07001750 * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
Dan Egnor661f0132010-02-19 11:23:00 -08001751 * <ul>
1752 * <li> {@link AuthenticatorException} if no authenticator was registered for
1753 * this account type or the authenticator failed to respond
1754 * <li> {@link OperationCanceledException} if the operation was canceled for
1755 * any reason, including the user canceling any operation
1756 * <li> {@link IOException} if the authenticator experienced an I/O problem
1757 * updating settings, usually because of network trouble
1758 * </ul>
Fred Quintana756b7352009-10-21 13:43:10 -07001759 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001760 public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
Fred Quintana33269202009-04-20 16:05:10 -07001761 final String accountType, final String authTokenType, final String[] features,
Dan Egnor661f0132010-02-19 11:23:00 -08001762 final Activity activity, final Bundle addAccountOptions,
Fred Quintana31957f12009-10-21 13:43:10 -07001763 final Bundle getAuthTokenOptions,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001764 final AccountManagerCallback<Bundle> callback, final Handler handler) {
Fred Quintana33269202009-04-20 16:05:10 -07001765 if (accountType == null) throw new IllegalArgumentException("account type is null");
1766 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001767 final GetAuthTokenByTypeAndFeaturesTask task =
1768 new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
Dan Egnor661f0132010-02-19 11:23:00 -08001769 activity, addAccountOptions, getAuthTokenOptions, callback, handler);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001770 task.start();
1771 return task;
Fred Quintana60307342009-03-24 22:48:12 -07001772 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001773
Fred Quintana1121bb52011-09-14 23:19:35 -07001774 /**
1775 * Returns an intent to an {@link Activity} that prompts the user to choose from a list of
1776 * accounts.
1777 * The caller will then typically start the activity by calling
1778 * <code>startActivityWithResult(intent, ...);</code>.
1779 * <p>
1780 * On success the activity returns a Bundle with the account name and type specified using
1781 * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
1782 * <p>
1783 * The most common case is to call this with one account type, e.g.:
1784 * <p>
Fred Quintanad88324d2011-09-19 11:43:05 -07001785 * <pre> newChooseAccountsIntent(null, null, new String[]{"com.google"}, false, null,
1786 * null, null, null);</pre>
Fred Quintana1121bb52011-09-14 23:19:35 -07001787 * @param selectedAccount if specified, indicates that the {@link Account} is the currently
1788 * selected one, according to the caller's definition of selected.
1789 * @param allowableAccounts an optional {@link ArrayList} of accounts that are allowed to be
1790 * shown. If not specified then this field will not limit the displayed accounts.
1791 * @param allowableAccountTypes an optional string array of account types. These are used
1792 * both to filter the shown accounts and to filter the list of account types that are shown
1793 * when adding an account.
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001794 * @param alwaysPromptForAccount if set the account chooser screen is always shown, otherwise
1795 * it is only shown when there is more than one account from which to choose
Fred Quintanad88324d2011-09-19 11:43:05 -07001796 * @param descriptionOverrideText if non-null this string is used as the description in the
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001797 * accounts chooser screen rather than the default
Fred Quintanad88324d2011-09-19 11:43:05 -07001798 * @param addAccountAuthTokenType this string is passed as the {@link #addAccount}
1799 * authTokenType parameter
1800 * @param addAccountRequiredFeatures this string array is passed as the {@link #addAccount}
1801 * requiredFeatures parameter
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001802 * @param addAccountOptions This {@link Bundle} is passed as the {@link #addAccount} options
Fred Quintanad88324d2011-09-19 11:43:05 -07001803 * parameter
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001804 * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
Fred Quintana1121bb52011-09-14 23:19:35 -07001805 */
1806 static public Intent newChooseAccountIntent(Account selectedAccount,
1807 ArrayList<Account> allowableAccounts,
1808 String[] allowableAccountTypes,
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001809 boolean alwaysPromptForAccount,
1810 String descriptionOverrideText,
1811 String addAccountAuthTokenType,
1812 String[] addAccountRequiredFeatures,
Fred Quintana1121bb52011-09-14 23:19:35 -07001813 Bundle addAccountOptions) {
1814 Intent intent = new Intent();
1815 intent.setClassName("android", "android.accounts.ChooseTypeAndAccountActivity");
1816 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
1817 allowableAccounts);
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001818 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
1819 allowableAccountTypes);
Fred Quintana1121bb52011-09-14 23:19:35 -07001820 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
1821 addAccountOptions);
1822 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount);
Fred Quintanab04fe4e2011-09-16 21:17:21 -07001823 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT,
1824 alwaysPromptForAccount);
1825 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_DESCRIPTION_TEXT_OVERRIDE,
1826 descriptionOverrideText);
1827 intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
1828 addAccountAuthTokenType);
1829 intent.putExtra(
1830 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
1831 addAccountRequiredFeatures);
Fred Quintana1121bb52011-09-14 23:19:35 -07001832 return intent;
1833 }
1834
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001835 private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
Fred Quintanad9d2f112009-04-23 13:36:27 -07001836 Maps.newHashMap();
1837
Fred Quintanad9d2f112009-04-23 13:36:27 -07001838 /**
1839 * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
1840 * so that it can read the updated list of accounts and send them to the listener
1841 * in mAccountsUpdatedListeners.
1842 */
1843 private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() {
1844 public void onReceive(final Context context, final Intent intent) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001845 final Account[] accounts = getAccounts();
1846 // send the result to the listeners
1847 synchronized (mAccountsUpdatedListeners) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001848 for (Map.Entry<OnAccountsUpdateListener, Handler> entry :
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001849 mAccountsUpdatedListeners.entrySet()) {
1850 postToHandler(entry.getValue(), entry.getKey(), accounts);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001851 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001852 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001853 }
1854 };
1855
1856 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001857 * Adds an {@link OnAccountsUpdateListener} to this instance of the
1858 * {@link AccountManager}. This listener will be notified whenever the
1859 * list of accounts on the device changes.
1860 *
1861 * <p>As long as this listener is present, the AccountManager instance
1862 * will not be garbage-collected, and neither will the {@link Context}
1863 * used to retrieve it, which may be a large Activity instance. To avoid
1864 * memory leaks, you must remove this listener before then. Normally
1865 * listeners are added in an Activity or Service's {@link Activity#onCreate}
1866 * and removed in {@link Activity#onDestroy}.
1867 *
1868 * <p>It is safe to call this method from the main thread.
1869 *
1870 * <p>No permission is required to call this method.
1871 *
1872 * @param listener The listener to send notifications to
1873 * @param handler {@link Handler} identifying the thread to use
1874 * for notifications, null for the main thread
1875 * @param updateImmediately If true, the listener will be invoked
1876 * (on the handler thread) right away with the current account list
Fred Quintanad9d2f112009-04-23 13:36:27 -07001877 * @throws IllegalArgumentException if listener is null
1878 * @throws IllegalStateException if listener was already added
1879 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001880 public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
Fred Quintanad9d2f112009-04-23 13:36:27 -07001881 Handler handler, boolean updateImmediately) {
1882 if (listener == null) {
1883 throw new IllegalArgumentException("the listener is null");
1884 }
1885 synchronized (mAccountsUpdatedListeners) {
1886 if (mAccountsUpdatedListeners.containsKey(listener)) {
1887 throw new IllegalStateException("this listener is already added");
1888 }
1889 final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
1890
1891 mAccountsUpdatedListeners.put(listener, handler);
1892
1893 if (wasEmpty) {
1894 // Register a broadcast receiver to monitor account changes
1895 IntentFilter intentFilter = new IntentFilter();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001896 intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
Costin Manolacheb6437242009-09-10 16:14:12 -07001897 // To recover from disk-full.
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001898 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001899 mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
1900 }
1901 }
1902
1903 if (updateImmediately) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001904 postToHandler(handler, listener, getAccounts());
Fred Quintanad9d2f112009-04-23 13:36:27 -07001905 }
1906 }
1907
1908 /**
Dan Egnor661f0132010-02-19 11:23:00 -08001909 * Removes an {@link OnAccountsUpdateListener} previously registered with
1910 * {@link #addOnAccountsUpdatedListener}. The listener will no longer
1911 * receive notifications of account changes.
1912 *
1913 * <p>It is safe to call this method from the main thread.
1914 *
1915 * <p>No permission is required to call this method.
1916 *
1917 * @param listener The previously added listener to remove
Fred Quintanad9d2f112009-04-23 13:36:27 -07001918 * @throws IllegalArgumentException if listener is null
1919 * @throws IllegalStateException if listener was not already added
1920 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001921 public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
Fred Quintana382601f2010-03-25 12:25:10 -07001922 if (listener == null) throw new IllegalArgumentException("listener is null");
Fred Quintanad9d2f112009-04-23 13:36:27 -07001923 synchronized (mAccountsUpdatedListeners) {
Bryan Mawhinney5be61f52009-09-24 14:50:25 +01001924 if (!mAccountsUpdatedListeners.containsKey(listener)) {
Costin Manolache88a211b2009-10-29 11:30:11 -07001925 Log.e(TAG, "Listener was not previously added");
1926 return;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001927 }
Bryan Mawhinney5be61f52009-09-24 14:50:25 +01001928 mAccountsUpdatedListeners.remove(listener);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001929 if (mAccountsUpdatedListeners.isEmpty()) {
1930 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
1931 }
1932 }
1933 }
Fred Quintana60307342009-03-24 22:48:12 -07001934}