blob: 43a0f303dedeefa3645c99cdb2989b9d2c7068a5 [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 Quintana60307342009-03-24 22:48:12 -070032
Fred Quintanaa698f422009-04-08 19:14:54 -070033import java.io.IOException;
34import java.util.concurrent.Callable;
35import java.util.concurrent.CancellationException;
36import java.util.concurrent.ExecutionException;
37import java.util.concurrent.FutureTask;
38import java.util.concurrent.TimeoutException;
39import java.util.concurrent.TimeUnit;
Fred Quintanad9d2f112009-04-23 13:36:27 -070040import java.util.HashMap;
41import java.util.Map;
42
43import com.google.android.collect.Maps;
Fred Quintana60307342009-03-24 22:48:12 -070044
45/**
Fred Quintana756b7352009-10-21 13:43:10 -070046 * A class that helps with interactions with the AccountManager Service. It provides
Fred Quintana60307342009-03-24 22:48:12 -070047 * methods to allow for account, password, and authtoken management for all accounts on the
Fred Quintana4db3a5b2009-10-05 17:19:03 -070048 * device. One accesses the {@link AccountManager} by calling:
Fred Quintana756b7352009-10-21 13:43:10 -070049 * <pre>
Fred Quintanaa698f422009-04-08 19:14:54 -070050 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana756b7352009-10-21 13:43:10 -070051 * </pre>
Fred Quintana60307342009-03-24 22:48:12 -070052 *
53 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -070054 * The AccountManager Service provides storage for the accounts known to the system,
55 * provides methods to manage them, and allows the registration of authenticators to
56 * which operations such as addAccount and getAuthToken are delegated.
57 * <p>
58 * Many of the calls take an {@link AccountManagerCallback} and {@link Handler} as parameters.
59 * These calls return immediately but run asynchronously. If a callback is provided then
60 * {@link AccountManagerCallback#run} will be invoked wen the request completes, successfully
61 * or not. An {@link AccountManagerFuture} is returned by these requests and also passed into the
62 * callback. The result if retrieved by calling {@link AccountManagerFuture#getResult()} which
63 * either returns the result or throws an exception as appropriate.
64 * <p>
65 * The asynchronous request can be made blocking by not providing a callback and instead
66 * calling {@link AccountManagerFuture#getResult()} on the future that is returned. This will
67 * cause the running thread to block until the result is returned. Keep in mind that one
68 * should not block the main thread in this way. Instead one should either use a callback,
69 * thus making the call asynchronous, or make the blocking call on a separate thread.
Fred Quintana8570f742010-02-18 10:32:54 -080070 * getResult() will throw an {@link IllegalStateException} if you call it from the main thread
71 * before the request has completed, i.e. before the callback has been invoked.
Fred Quintana756b7352009-10-21 13:43:10 -070072 * <p>
73 * If one wants to ensure that the callback is invoked from a specific handler then they should
74 * pass the handler to the request. This makes it easier to ensure thread-safety by running
75 * all of one's logic from a single handler.
Fred Quintana60307342009-03-24 22:48:12 -070076 */
77public class AccountManager {
78 private static final String TAG = "AccountManager";
79
Fred Quintanaf7ae77c2009-10-02 17:19:31 -070080 public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
81 public static final int ERROR_CODE_NETWORK_ERROR = 3;
82 public static final int ERROR_CODE_CANCELED = 4;
83 public static final int ERROR_CODE_INVALID_RESPONSE = 5;
84 public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
85 public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
86 public static final int ERROR_CODE_BAD_REQUEST = 8;
Fred Quintana756b7352009-10-21 13:43:10 -070087
Fred Quintanaf7ae77c2009-10-02 17:19:31 -070088 public static final String KEY_ACCOUNTS = "accounts";
89 public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
90 public static final String KEY_USERDATA = "userdata";
91 public static final String KEY_AUTHTOKEN = "authtoken";
92 public static final String KEY_PASSWORD = "password";
93 public static final String KEY_ACCOUNT_NAME = "authAccount";
94 public static final String KEY_ACCOUNT_TYPE = "accountType";
95 public static final String KEY_ERROR_CODE = "errorCode";
96 public static final String KEY_ERROR_MESSAGE = "errorMessage";
97 public static final String KEY_INTENT = "intent";
98 public static final String KEY_BOOLEAN_RESULT = "booleanResult";
99 public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
100 public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
101 public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage";
102 public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey";
103 public static final String ACTION_AUTHENTICATOR_INTENT =
104 "android.accounts.AccountAuthenticator";
105 public static final String AUTHENTICATOR_META_DATA_NAME =
106 "android.accounts.AccountAuthenticator";
107 public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
108
Fred Quintana60307342009-03-24 22:48:12 -0700109 private final Context mContext;
110 private final IAccountManager mService;
Fred Quintanad9d2f112009-04-23 13:36:27 -0700111 private final Handler mMainHandler;
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700112 /**
113 * Action sent as a broadcast Intent by the AccountsService
114 * when accounts are added to and/or removed from the device's
115 * database.
116 */
117 public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
118 "android.accounts.LOGIN_ACCOUNTS_CHANGED";
Fred Quintana60307342009-03-24 22:48:12 -0700119
Fred Quintana33269202009-04-20 16:05:10 -0700120 /**
121 * @hide
122 */
Fred Quintana60307342009-03-24 22:48:12 -0700123 public AccountManager(Context context, IAccountManager service) {
124 mContext = context;
125 mService = service;
Fred Quintanad9d2f112009-04-23 13:36:27 -0700126 mMainHandler = new Handler(mContext.getMainLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700127 }
128
Fred Quintana0eabf022009-04-27 15:08:17 -0700129 /**
130 * @hide used for testing only
131 */
132 public AccountManager(Context context, IAccountManager service, Handler handler) {
133 mContext = context;
134 mService = service;
135 mMainHandler = handler;
136 }
137
Fred Quintana756b7352009-10-21 13:43:10 -0700138 /**
139 * Retrieve an AccountManager instance that is associated with the context that is passed in.
140 * Certain calls such as {@link #addOnAccountsUpdatedListener} use this context internally,
141 * so the caller must take care to use a {@link Context} whose lifetime is associated with
142 * the listener registration.
143 * @param context The {@link Context} to use when necessary
144 * @return an {@link AccountManager} instance that is associated with context
145 */
Fred Quintanaa698f422009-04-08 19:14:54 -0700146 public static AccountManager get(Context context) {
147 return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
148 }
149
Fred Quintana756b7352009-10-21 13:43:10 -0700150 /**
151 * Get the password that is associated with the account. Returns null if the account does
152 * not exist.
153 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800154 * It is safe to call this method from the main thread.
155 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700156 * Requires that the caller has permission
157 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
158 * with the same UID as the Authenticator for the account.
159 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700160 public String getPassword(final Account account) {
Fred Quintana60307342009-03-24 22:48:12 -0700161 try {
162 return mService.getPassword(account);
163 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700164 // will never happen
Fred Quintana60307342009-03-24 22:48:12 -0700165 throw new RuntimeException(e);
166 }
167 }
168
Fred Quintana756b7352009-10-21 13:43:10 -0700169 /**
170 * Get the user data named by "key" that is associated with the account.
171 * Returns null if the account does not exist or if it does not have a value for key.
172 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800173 * It is safe to call this method from the main thread.
174 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700175 * Requires that the caller has permission
176 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
177 * with the same UID as the Authenticator for the account.
178 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700179 public String getUserData(final Account account, final String key) {
Fred Quintana60307342009-03-24 22:48:12 -0700180 try {
181 return mService.getUserData(account, key);
182 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700183 // will never happen
Fred Quintana60307342009-03-24 22:48:12 -0700184 throw new RuntimeException(e);
185 }
186 }
187
Fred Quintana756b7352009-10-21 13:43:10 -0700188 /**
189 * Query the AccountManager Service for an array that contains a
190 * {@link AuthenticatorDescription} for each registered authenticator.
191 * @return an array that contains all the authenticators known to the AccountManager service.
192 * This array will be empty if there are no authenticators and will never return null.
193 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800194 * It is safe to call this method from the main thread.
195 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700196 * No permission is required to make this call.
197 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700198 public AuthenticatorDescription[] getAuthenticatorTypes() {
Fred Quintanaa698f422009-04-08 19:14:54 -0700199 try {
200 return mService.getAuthenticatorTypes();
201 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700202 // will never happen
Fred Quintanaa698f422009-04-08 19:14:54 -0700203 throw new RuntimeException(e);
204 }
205 }
206
Fred Quintana756b7352009-10-21 13:43:10 -0700207 /**
208 * Query the AccountManager Service for all accounts.
209 * @return an array that contains all the accounts known to the AccountManager service.
210 * This array will be empty if there are no accounts and will never return null.
211 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800212 * It is safe to call this method from the main thread.
213 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700214 * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}
215 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700216 public Account[] getAccounts() {
Fred Quintana60307342009-03-24 22:48:12 -0700217 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700218 return mService.getAccounts(null);
Fred Quintana60307342009-03-24 22:48:12 -0700219 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700220 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700221 throw new RuntimeException(e);
222 }
223 }
224
Fred Quintana756b7352009-10-21 13:43:10 -0700225 /**
226 * Query the AccountManager for the set of accounts that have a given type. If null
227 * is passed as the type than all accounts are returned.
228 * @param type the account type by which to filter, or null to get all accounts
229 * @return an array that contains the accounts that match the specified type. This array
230 * will be empty if no accounts match. It will never return null.
231 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800232 * It is safe to call this method from the main thread.
233 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700234 * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}
235 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700236 public Account[] getAccountsByType(String type) {
Fred Quintana60307342009-03-24 22:48:12 -0700237 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700238 return mService.getAccounts(type);
Fred Quintana60307342009-03-24 22:48:12 -0700239 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700240 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700241 throw new RuntimeException(e);
242 }
243 }
244
Fred Quintana756b7352009-10-21 13:43:10 -0700245 /**
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800246 * Tests that the given account has the specified features. If this account does not exist
247 * then this call returns false.
248 * <p>
249 * This call returns immediately but runs asynchronously and the result is accessed via the
250 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
251 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
252 * method asynchronously then they will generally pass in a callback object that will get
253 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
254 * they will generally pass null for the callback and instead call
255 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
256 * which will then block until the request completes.
257 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800258 * Do not block the main thread waiting this method's result.
259 * <p>
260 * Not allowed from main thread (but allowed from other threads):
261 * <pre>
262 * Boolean result = hasFeatures(account, features, callback, handler).getResult();
263 * </pre>
264 * Allowed from main thread:
265 * <pre>
266 * hasFeatures(account, features, new AccountManagerCallback<Boolean>() {
267 * public void run(AccountManagerFuture<Boolean> future) {
268 * Boolean result = future.getResult();
269 * // use result
270 * }
271 * }, handler);
272 * </pre>
273 * <p>
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800274 * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
275 *
276 * @param account The {@link Account} to test
277 * @param features the features for which to test
278 * @param callback A callback to invoke when the request completes. If null then
279 * no callback is invoked.
280 * @param handler The {@link Handler} to use to invoke the callback. If null then the
281 * main thread's {@link Handler} is used.
282 * @return an {@link AccountManagerFuture} that represents the future result of the call.
283 * The future result is a {@link Boolean} that is true if the account exists and has the
284 * specified features.
285 */
Fred Quintana3084a6f2010-01-14 18:02:03 -0800286 public AccountManagerFuture<Boolean> hasFeatures(final Account account,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800287 final String[] features,
288 AccountManagerCallback<Boolean> callback, Handler handler) {
289 return new Future2Task<Boolean>(handler, callback) {
290 public void doWork() throws RemoteException {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800291 mService.hasFeatures(mResponse, account, features);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800292 }
293 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
294 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
295 throw new AuthenticatorException("no result in response");
296 }
297 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
298 }
299 }.start();
300 }
301
302 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800303 * Add an account to the AccountManager's set of known accounts.
Fred Quintana756b7352009-10-21 13:43:10 -0700304 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800305 * It is safe to call this method from the main thread.
306 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700307 * Requires that the caller has permission
308 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
309 * with the same UID as the Authenticator for the account.
310 * @param account The account to add
311 * @param password The password to associate with the account. May be null.
Fred Quintana31957f12009-10-21 13:43:10 -0700312 * @param userdata A bundle of key/value pairs to set as the account's userdata. May be null.
Fred Quintana756b7352009-10-21 13:43:10 -0700313 * @return true if the account was sucessfully added, false otherwise, for example,
314 * if the account already exists or if the account is null
315 */
Fred Quintana31957f12009-10-21 13:43:10 -0700316 public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
Fred Quintana60307342009-03-24 22:48:12 -0700317 try {
Fred Quintana31957f12009-10-21 13:43:10 -0700318 return mService.addAccount(account, password, userdata);
Fred Quintana60307342009-03-24 22:48:12 -0700319 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700320 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700321 throw new RuntimeException(e);
322 }
323 }
324
Fred Quintana756b7352009-10-21 13:43:10 -0700325 /**
326 * Removes the given account. If this account does not exist then this call has no effect.
327 * <p>
328 * This call returns immediately but runs asynchronously and the result is accessed via the
329 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
330 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
331 * method asynchronously then they will generally pass in a callback object that will get
332 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
333 * they will generally pass null for the callback and instead call
334 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
335 * which will then block until the request completes.
336 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800337 * Do not block the main thread waiting this method's result.
338 * <p>
339 * Not allowed from main thread (but allowed from other threads):
340 * <pre>
341 * Boolean result = removeAccount(account, callback, handler).getResult();
342 * </pre>
343 * Allowed from main thread:
344 * <pre>
345 * removeAccount(account, new AccountManagerCallback<Boolean>() {
346 * public void run(AccountManagerFuture<Boolean> future) {
347 * Boolean result = future.getResult();
348 * // use result
349 * }
350 * }, handler);
351 * </pre>
352 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700353 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
354 *
355 * @param account The {@link Account} to remove
356 * @param callback A callback to invoke when the request completes. If null then
357 * no callback is invoked.
358 * @param handler The {@link Handler} to use to invoke the callback. If null then the
359 * main thread's {@link Handler} is used.
360 * @return an {@link AccountManagerFuture} that represents the future result of the call.
361 * The future result is a {@link Boolean} that is true if the account is successfully removed
362 * or false if the authenticator refuses to remove the account.
363 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700364 public AccountManagerFuture<Boolean> removeAccount(final Account account,
365 AccountManagerCallback<Boolean> callback, Handler handler) {
366 return new Future2Task<Boolean>(handler, callback) {
367 public void doWork() throws RemoteException {
368 mService.removeAccount(mResponse, account);
Fred Quintanaa698f422009-04-08 19:14:54 -0700369 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700370 public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700371 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700372 throw new AuthenticatorException("no result in response");
373 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700374 return bundle.getBoolean(KEY_BOOLEAN_RESULT);
Fred Quintanaa698f422009-04-08 19:14:54 -0700375 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700376 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700377 }
378
Fred Quintana756b7352009-10-21 13:43:10 -0700379 /**
380 * Removes the given authtoken. If this authtoken does not exist for the given account type
381 * then this call has no effect.
382 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800383 * It is safe to call this method from the main thread.
384 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700385 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
386 * @param accountType the account type of the authtoken to invalidate
387 * @param authToken the authtoken to invalidate
388 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700389 public void invalidateAuthToken(final String accountType, final String authToken) {
Fred Quintana60307342009-03-24 22:48:12 -0700390 try {
391 mService.invalidateAuthToken(accountType, authToken);
392 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700393 // won't ever happen
Fred Quintana60307342009-03-24 22:48:12 -0700394 throw new RuntimeException(e);
395 }
396 }
397
Fred Quintana756b7352009-10-21 13:43:10 -0700398 /**
399 * Gets the authtoken named by "authTokenType" for the specified account if it is cached
400 * by the AccountManager. If no authtoken is cached then null is returned rather than
401 * asking the authenticaticor to generate one. If the account or the
402 * authtoken do not exist then null is returned.
403 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800404 * It is safe to call this method from the main thread.
405 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700406 * Requires that the caller has permission
407 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
408 * with the same UID as the Authenticator for the account.
409 * @param account the account whose authtoken is to be retrieved, must not be null
410 * @param authTokenType the type of authtoken to retrieve
411 * @return an authtoken for the given account and authTokenType, if one is cached by the
412 * AccountManager, null otherwise.
413 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700414 public String peekAuthToken(final Account account, final String authTokenType) {
Fred Quintana31957f12009-10-21 13:43:10 -0700415 if (account == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -0700416 Log.e(TAG, "peekAuthToken: the account must not be null");
417 return null;
Fred Quintana31957f12009-10-21 13:43:10 -0700418 }
419 if (authTokenType == null) {
420 return null;
421 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700422 try {
423 return mService.peekAuthToken(account, authTokenType);
424 } catch (RemoteException e) {
425 // won't ever happen
426 throw new RuntimeException(e);
427 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700428 }
429
Fred Quintana756b7352009-10-21 13:43:10 -0700430 /**
431 * Sets the password for the account. The password may be null. If the account does not exist
432 * then this call has no affect.
433 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800434 * It is safe to call this method from the main thread.
435 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700436 * Requires that the caller has permission
437 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
438 * with the same UID as the Authenticator for the account.
439 * @param account the account whose password is to be set. Must not be null.
440 * @param password the password to set for the account. May be null.
441 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700442 public void setPassword(final Account account, final String password) {
Fred Quintana31957f12009-10-21 13:43:10 -0700443 if (account == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -0700444 Log.e(TAG, "the account must not be null");
445 return;
Fred Quintana31957f12009-10-21 13:43:10 -0700446 }
Fred Quintana60307342009-03-24 22:48:12 -0700447 try {
448 mService.setPassword(account, password);
449 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700450 // won't ever happen
451 throw new RuntimeException(e);
Fred Quintana60307342009-03-24 22:48:12 -0700452 }
453 }
454
Fred Quintana756b7352009-10-21 13:43:10 -0700455 /**
456 * Sets the password for account to null. If the account does not exist then this call
457 * has no effect.
458 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800459 * It is safe to call this method from the main thread.
460 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700461 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
462 * @param account the account whose password is to be cleared. Must not be null.
463 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700464 public void clearPassword(final Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700465 if (account == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -0700466 Log.e(TAG, "the account must not be null");
467 return;
Fred Quintana31957f12009-10-21 13:43:10 -0700468 }
Fred Quintana60307342009-03-24 22:48:12 -0700469 try {
470 mService.clearPassword(account);
471 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700472 // won't ever happen
473 throw new RuntimeException(e);
Fred Quintana60307342009-03-24 22:48:12 -0700474 }
475 }
476
Fred Quintana756b7352009-10-21 13:43:10 -0700477 /**
478 * Sets account's userdata named "key" to the specified value. If the account does not
479 * exist then this call has no effect.
480 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800481 * It is safe to call this method from the main thread.
482 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700483 * Requires that the caller has permission
484 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
485 * with the same UID as the Authenticator for the account.
486 * @param account the account whose userdata is to be set. Must not be null.
487 * @param key the key of the userdata to set. Must not be null.
488 * @param value the value to set. May be null.
489 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700490 public void setUserData(final Account account, final String key, final String value) {
Fred Quintana31957f12009-10-21 13:43:10 -0700491 if (account == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -0700492 Log.e(TAG, "the account must not be null");
493 return;
Fred Quintana31957f12009-10-21 13:43:10 -0700494 }
495 if (key == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -0700496 Log.e(TAG, "the key must not be null");
497 return;
Fred Quintana31957f12009-10-21 13:43:10 -0700498 }
Fred Quintana60307342009-03-24 22:48:12 -0700499 try {
500 mService.setUserData(account, key, value);
501 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700502 // won't ever happen
503 throw new RuntimeException(e);
Fred Quintana60307342009-03-24 22:48:12 -0700504 }
505 }
506
Fred Quintana756b7352009-10-21 13:43:10 -0700507 /**
508 * Sets the authtoken named by "authTokenType" to the value specified by authToken.
509 * If the account does not exist then this call has no effect.
510 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800511 * It is safe to call this method from the main thread.
512 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700513 * Requires that the caller has permission
514 * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
515 * with the same UID as the Authenticator for the account.
516 * @param account the account whose authtoken is to be set. Must not be null.
517 * @param authTokenType the type of the authtoken to set. Must not be null.
518 * @param authToken the authToken to set. May be null.
519 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700520 public void setAuthToken(Account account, final String authTokenType, final String authToken) {
Fred Quintana60307342009-03-24 22:48:12 -0700521 try {
522 mService.setAuthToken(account, authTokenType, authToken);
523 } catch (RemoteException e) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700524 // won't ever happen
525 throw new RuntimeException(e);
Fred Quintana60307342009-03-24 22:48:12 -0700526 }
527 }
528
Fred Quintana756b7352009-10-21 13:43:10 -0700529 /**
530 * Convenience method that makes a blocking call to
531 * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}
532 * then extracts and returns the value of {@link #KEY_AUTHTOKEN} from its result.
533 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800534 * It is not safe to call this method from the main thread. See {@link #getAuthToken}.
535 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700536 * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
537 * @param account the account whose authtoken is to be retrieved, must not be null
538 * @param authTokenType the type of authtoken to retrieve
539 * @param notifyAuthFailure if true, cause the AccountManager to put up a "sign-on" notification
540 * for the account if no authtoken is cached by the AccountManager and the the authenticator
541 * does not have valid credentials to get an authtoken.
542 * @return an authtoken for the given account and authTokenType, if one is cached by the
543 * AccountManager, null otherwise.
544 * @throws AuthenticatorException if the authenticator is not present, unreachable or returns
545 * an invalid response.
546 * @throws OperationCanceledException if the request is canceled for any reason
547 * @throws java.io.IOException if the authenticator experiences an IOException while attempting
548 * to communicate with its backend server.
549 */
Fred Quintanaa698f422009-04-08 19:14:54 -0700550 public String blockingGetAuthToken(Account account, String authTokenType,
551 boolean notifyAuthFailure)
552 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintanaa698f422009-04-08 19:14:54 -0700553 Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
554 null /* handler */).getResult();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700555 return bundle.getString(KEY_AUTHTOKEN);
Fred Quintanaa698f422009-04-08 19:14:54 -0700556 }
557
558 /**
Fred Quintana756b7352009-10-21 13:43:10 -0700559 * Request that an authtoken of the specified type be returned for an account.
560 * If the Account Manager has a cached authtoken of the requested type then it will
561 * service the request itself. Otherwise it will pass the request on to the authenticator.
562 * The authenticator can try to service this request with information it already has stored
563 * in the AccountManager but may need to launch an activity to prompt the
564 * user to enter credentials. If it is able to retrieve the authtoken it will be returned
565 * in the result.
566 * <p>
567 * If the authenticator needs to prompt the user for credentials it will return an intent to
Fred Quintana8570f742010-02-18 10:32:54 -0800568 * an activity that will do the prompting. The supplied activity will be used to launch the
569 * intent and the result will come from the launched activity.
Fred Quintana756b7352009-10-21 13:43:10 -0700570 * <p>
571 * This call returns immediately but runs asynchronously and the result is accessed via the
572 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
573 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
574 * method asynchronously then they will generally pass in a callback object that will get
575 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
576 * they will generally pass null for the callback and instead call
577 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
578 * which will then block until the request completes.
579 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800580 * Do not block the main thread waiting this method's result.
581 * <p>
582 * Not allowed from main thread (but allowed from other threads):
583 * <pre>
584 * Bundle result = getAuthToken(
585 * account, authTokenType, options, activity, callback, handler).getResult();
586 * </pre>
587 * Allowed from main thread:
588 * <pre>
589 * getAuthToken(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() {
590 * public void run(AccountManagerFuture<Bundle> future) {
591 * Bundle result = future.getResult();
592 * // use result
593 * }
594 * }, handler);
595 * </pre>
596 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700597 * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
Fred Quintanaa698f422009-04-08 19:14:54 -0700598 *
Fred Quintana756b7352009-10-21 13:43:10 -0700599 * @param account The account whose credentials are to be updated.
600 * @param authTokenType the auth token to retrieve as part of updating the credentials.
601 * May be null.
Fred Quintana31957f12009-10-21 13:43:10 -0700602 * @param options authenticator specific options for the request
Fred Quintana756b7352009-10-21 13:43:10 -0700603 * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
Fred Quintana8570f742010-02-18 10:32:54 -0800604 * the intent will be started with this activity. If you do not with to have the intent
605 * started automatically then use the other form,
606 * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, android.os.Handler)}
Fred Quintana756b7352009-10-21 13:43:10 -0700607 * @param callback A callback to invoke when the request completes. If null then
608 * no callback is invoked.
609 * @param handler The {@link Handler} to use to invoke the callback. If null then the
610 * main thread's {@link Handler} is used.
611 * @return an {@link AccountManagerFuture} that represents the future result of the call.
612 * The future result is a {@link Bundle} that contains:
613 * <ul>
614 * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN}
615 * </ul>
616 * If the user presses "back" then the request will be canceled.
Fred Quintanaa698f422009-04-08 19:14:54 -0700617 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700618 public AccountManagerFuture<Bundle> getAuthToken(
Fred Quintana31957f12009-10-21 13:43:10 -0700619 final Account account, final String authTokenType, final Bundle options,
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700620 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700621 if (activity == null) throw new IllegalArgumentException("activity is null");
622 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
623 return new AmsTask(activity, handler, callback) {
624 public void doWork() throws RemoteException {
625 mService.getAuthToken(mResponse, account, authTokenType,
626 false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
Fred Quintana31957f12009-10-21 13:43:10 -0700627 options);
Fred Quintanaa698f422009-04-08 19:14:54 -0700628 }
Fred Quintana33269202009-04-20 16:05:10 -0700629 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700630 }
631
Fred Quintana756b7352009-10-21 13:43:10 -0700632 /**
633 * Request that an authtoken of the specified type be returned for an account.
634 * If the Account Manager has a cached authtoken of the requested type then it will
635 * service the request itself. Otherwise it will pass the request on to the authenticator.
636 * The authenticator can try to service this request with information it already has stored
637 * in the AccountManager but may need to launch an activity to prompt the
638 * user to enter credentials. If it is able to retrieve the authtoken it will be returned
639 * in the result.
640 * <p>
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800641 * If the authenticator needs to prompt the user for credentials, rather than returning the
642 * authtoken it will instead return an intent for
Fred Quintana756b7352009-10-21 13:43:10 -0700643 * an activity that will do the prompting. If an intent is returned and notifyAuthFailure
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800644 * is true then a notification will be created that launches this intent. This intent can be
645 * invoked by the caller directly to start the activity that prompts the user for the
646 * updated credentials. Otherwise this activity will not be run until the user activates
647 * the notification.
Fred Quintana756b7352009-10-21 13:43:10 -0700648 * <p>
649 * This call returns immediately but runs asynchronously and the result is accessed via the
650 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
651 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
652 * method asynchronously then they will generally pass in a callback object that will get
653 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
654 * they will generally pass null for the callback and instead call
655 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
656 * which will then block until the request completes.
657 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800658 * Do not block the main thread waiting this method's result.
659 * <p>
660 * Not allowed from main thread (but allowed from other threads):
661 * <pre>
662 * Bundle result = getAuthToken(
663 * account, authTokenType, notifyAuthFailure, callback, handler).getResult();
664 * </pre>
665 * Allowed from main thread:
666 * <pre>
667 * getAuthToken(account, authTokenType, notifyAuthFailure, new AccountManagerCallback<Bundle>() {
668 * public void run(AccountManagerFuture<Bundle> future) {
669 * Bundle result = future.getResult();
670 * // use result
671 * }
672 * }, handler);
673 * </pre>
674 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700675 * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
676 *
677 * @param account The account whose credentials are to be updated.
678 * @param authTokenType the auth token to retrieve as part of updating the credentials.
679 * May be null.
680 * @param notifyAuthFailure if true and the authenticator returns a {@link #KEY_INTENT} in the
681 * result then a "sign-on needed" notification will be created that will launch this intent.
682 * @param callback A callback to invoke when the request completes. If null then
683 * no callback is invoked.
684 * @param handler The {@link Handler} to use to invoke the callback. If null then the
685 * main thread's {@link Handler} is used.
686 * @return an {@link AccountManagerFuture} that represents the future result of the call.
687 * The future result is a {@link Bundle} that contains either:
688 * <ul>
689 * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
690 * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN}
691 * if the authenticator is able to retrieve the auth token
692 * </ul>
693 * If the user presses "back" then the request will be canceled.
694 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700695 public AccountManagerFuture<Bundle> getAuthToken(
Fred Quintanaa698f422009-04-08 19:14:54 -0700696 final Account account, final String authTokenType, final boolean notifyAuthFailure,
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700697 AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700698 if (account == null) throw new IllegalArgumentException("account is null");
699 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
700 return new AmsTask(null, handler, callback) {
701 public void doWork() throws RemoteException {
702 mService.getAuthToken(mResponse, account, authTokenType,
703 notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
704 }
Fred Quintana33269202009-04-20 16:05:10 -0700705 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700706 }
707
Fred Quintana756b7352009-10-21 13:43:10 -0700708 /**
709 * Request that an account be added with the given accountType. This request
710 * is processed by the authenticator for the account type. If no authenticator is registered
711 * in the system then {@link AuthenticatorException} is thrown.
712 * <p>
713 * This call returns immediately but runs asynchronously and the result is accessed via the
714 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
715 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
716 * method asynchronously then they will generally pass in a callback object that will get
717 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
718 * they will generally pass null for the callback and instead call
719 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
720 * which will then block until the request completes.
721 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800722 * Do not block the main thread waiting this method's result.
723 * <p>
724 * Not allowed from main thread (but allowed from other threads):
725 * <pre>
726 * Bundle result = addAccount(
727 * account, authTokenType, features, options, activity, callback, handler).getResult();
728 * </pre>
729 * Allowed from main thread:
730 * <pre>
731 * addAccount(account, authTokenType, features, options, activity, new AccountManagerCallback<Bundle>() {
732 * public void run(AccountManagerFuture<Bundle> future) {
733 * Bundle result = future.getResult();
734 * // use result
735 * }
736 * }, handler);
737 * </pre>
738 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700739 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
740 *
741 * @param accountType The type of account to add. This must not be null.
742 * @param authTokenType The account that is added should be able to service this auth token
743 * type. This may be null.
744 * @param requiredFeatures The account that is added should support these features.
745 * This array may be null or empty.
746 * @param addAccountOptions A bundle of authenticator-specific options that is passed on
747 * to the authenticator. This may be null.
748 * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
749 * the intent will be started with this activity. If activity is null then the result will
750 * be returned as-is.
751 * @param callback A callback to invoke when the request completes. If null then
752 * no callback is invoked.
753 * @param handler The {@link Handler} to use to invoke the callback. If null then the
754 * main thread's {@link Handler} is used.
755 * @return an {@link AccountManagerFuture} that represents the future result of the call.
756 * The future result is a {@link Bundle} that contains either:
757 * <ul>
758 * <li> {@link #KEY_INTENT}, or
759 * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE}
Fred Quintana756b7352009-10-21 13:43:10 -0700760 * </ul>
761 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700762 public AccountManagerFuture<Bundle> addAccount(final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -0700763 final String authTokenType, final String[] requiredFeatures,
764 final Bundle addAccountOptions,
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700765 final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700766 return new AmsTask(activity, handler, callback) {
767 public void doWork() throws RemoteException {
Costin Manolache88a211b2009-10-29 11:30:11 -0700768 if (accountType == null) {
769 Log.e(TAG, "the account must not be null");
770 // to unblock caller waiting on Future.get()
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800771 set(new Bundle());
Costin Manolache88a211b2009-10-29 11:30:11 -0700772 return;
773 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700774 mService.addAcount(mResponse, accountType, authTokenType,
Fred Quintana33269202009-04-20 16:05:10 -0700775 requiredFeatures, activity != null, addAccountOptions);
Fred Quintanaa698f422009-04-08 19:14:54 -0700776 }
Fred Quintana33269202009-04-20 16:05:10 -0700777 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700778 }
779
Fred Quintana8570f742010-02-18 10:32:54 -0800780 /**
781 * Queries for accounts that match the given account type and feature set.
782 * <p>
783 * This call returns immediately but runs asynchronously and the result is accessed via the
784 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
785 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
786 * method asynchronously then they will generally pass in a callback object that will get
787 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
788 * they will generally pass null for the callback and instead call
789 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
790 * which will then block until the request completes.
791 * <p>
792 * Do not block the main thread waiting this method's result.
793 * <p>
794 * Not allowed from main thread (but allowed from other threads):
795 * <pre>
796 * Account[] result =
797 * getAccountsByTypeAndFeatures(accountType, features, callback, handler).getResult();
798 * </pre>
799 * Allowed from main thread:
800 * <pre>
801 * getAccountsByTypeAndFeatures(accountType, features, new AccountManagerCallback<Account[]>() {
802 * public void run(AccountManagerFuture<Account[]> future) {
803 * Account[] result = future.getResult();
804 * // use result
805 * }
806 * }, handler);
807 * </pre>
808 * <p>
809 * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
810 *
811 * @param type The type of {@link Account} to return. If null is passed in then an empty
812 * array will be returned.
813 * @param features the features with which to filter the accounts list. Each returned account
814 * will have all specified features. This may be null, which will mean the account list will
815 * not be filtered by features, making this functionally identical to
816 * {@link #getAccountsByType(String)}.
817 * @param callback A callback to invoke when the request completes. If null then
818 * no callback is invoked.
819 * @param handler The {@link Handler} to use to invoke the callback. If null then the
820 * main thread's {@link Handler} is used.
821 * @return an {@link AccountManagerFuture} that represents the future result of the call.
822 * The future result is a an {@link Account} array that contains accounts of the specified
823 * type that match all the requested features.
824 */
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700825 public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
826 final String type, final String[] features,
827 AccountManagerCallback<Account[]> callback, Handler handler) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700828 return new Future2Task<Account[]>(handler, callback) {
829 public void doWork() throws RemoteException {
Costin Manolache88a211b2009-10-29 11:30:11 -0700830 if (type == null) {
831 Log.e(TAG, "Type is null");
832 set(new Account[0]);
833 return;
834 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700835 mService.getAccountsByFeatures(mResponse, type, features);
836 }
837 public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700838 if (!bundle.containsKey(KEY_ACCOUNTS)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700839 throw new AuthenticatorException("no result in response");
840 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700841 final Parcelable[] parcelables = bundle.getParcelableArray(KEY_ACCOUNTS);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700842 Account[] descs = new Account[parcelables.length];
843 for (int i = 0; i < parcelables.length; i++) {
844 descs[i] = (Account) parcelables[i];
845 }
846 return descs;
847 }
848 }.start();
849 }
850
Fred Quintana756b7352009-10-21 13:43:10 -0700851 /**
852 * Requests that the authenticator checks that the user knows the credentials for the account.
853 * This is typically done by returning an intent to an activity that prompts the user to
854 * enter the credentials. This request
855 * is processed by the authenticator for the account. If no matching authenticator is
856 * registered in the system then {@link AuthenticatorException} is thrown.
857 * <p>
858 * This call returns immediately but runs asynchronously and the result is accessed via the
859 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
860 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
861 * method asynchronously then they will generally pass in a callback object that will get
862 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
863 * they will generally pass null for the callback and instead call
864 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
865 * which will then block until the request completes.
866 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800867 * Do not block the main thread waiting this method's result.
868 * <p>
869 * Not allowed from main thread (but allowed from other threads):
870 * <pre>
871 * Bundle result = confirmCredentials(
872 * account, options, activity, callback, handler).getResult();
873 * </pre>
874 * Allowed from main thread:
875 * <pre>
876 * confirmCredentials(account, options, activity, new AccountManagerCallback<Bundle>() {
877 * public void run(AccountManagerFuture<Bundle> future) {
878 * Bundle result = future.getResult();
879 * // use result
880 * }
881 * }, handler);
882 * </pre>
883 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700884 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
885 *
886 * @param account The account whose credentials are to be checked
887 * @param options authenticator specific options for the request
888 * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
889 * the intent will be started with this activity. If activity is null then the result will
890 * be returned as-is.
891 * @param callback A callback to invoke when the request completes. If null then
892 * no callback is invoked.
893 * @param handler The {@link Handler} to use to invoke the callback. If null then the
894 * main thread's {@link Handler} is used.
895 * @return an {@link AccountManagerFuture} that represents the future result of the call.
896 * The future result is a {@link Bundle} that contains either:
897 * <ul>
898 * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
899 * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct
900 * credentials
901 * </ul>
902 * If the user presses "back" then the request will be canceled.
903 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700904 public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
905 final Bundle options,
906 final Activity activity,
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700907 final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -0700908 final Handler handler) {
909 return new AmsTask(activity, handler, callback) {
910 public void doWork() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700911 mService.confirmCredentials(mResponse, account, options, activity != null);
Fred Quintanaa698f422009-04-08 19:14:54 -0700912 }
Fred Quintana33269202009-04-20 16:05:10 -0700913 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700914 }
915
Fred Quintana756b7352009-10-21 13:43:10 -0700916 /**
917 * Requests that the authenticator update the the credentials for a user. This is typically
918 * done by returning an intent to an activity that will prompt the user to update the stored
919 * credentials for the account. This request
920 * is processed by the authenticator for the account. If no matching authenticator is
921 * registered in the system then {@link AuthenticatorException} is thrown.
922 * <p>
923 * This call returns immediately but runs asynchronously and the result is accessed via the
924 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
925 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
926 * method asynchronously then they will generally pass in a callback object that will get
927 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
928 * they will generally pass null for the callback and instead call
929 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
930 * which will then block until the request completes.
931 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800932 * Do not block the main thread waiting this method's result.
933 * <p>
934 * Not allowed from main thread (but allowed from other threads):
935 * <pre>
936 * Bundle result = updateCredentials(
937 * account, authTokenType, options, activity, callback, handler).getResult();
938 * </pre>
939 * Allowed from main thread:
940 * <pre>
941 * updateCredentials(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() {
942 * public void run(AccountManagerFuture<Bundle> future) {
943 * Bundle result = future.getResult();
944 * // use result
945 * }
946 * }, handler);
947 * </pre>
948 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -0700949 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
950 *
951 * @param account The account whose credentials are to be updated.
952 * @param authTokenType the auth token to retrieve as part of updating the credentials.
953 * May be null.
Fred Quintana31957f12009-10-21 13:43:10 -0700954 * @param options authenticator specific options for the request
Fred Quintana756b7352009-10-21 13:43:10 -0700955 * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
956 * the intent will be started with this activity. If activity is null then the result will
957 * be returned as-is.
958 * @param callback A callback to invoke when the request completes. If null then
959 * no callback is invoked.
960 * @param handler The {@link Handler} to use to invoke the callback. If null then the
961 * main thread's {@link Handler} is used.
962 * @return an {@link AccountManagerFuture} that represents the future result of the call.
963 * The future result is a {@link Bundle} that contains either:
964 * <ul>
965 * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
966 * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct
Fred Quintana8570f742010-02-18 10:32:54 -0800967 * credentials.
Fred Quintana756b7352009-10-21 13:43:10 -0700968 * </ul>
969 * If the user presses "back" then the request will be canceled.
970 */
971 public AccountManagerFuture<Bundle> updateCredentials(final Account account,
972 final String authTokenType,
Fred Quintana31957f12009-10-21 13:43:10 -0700973 final Bundle options, final Activity activity,
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700974 final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -0700975 final Handler handler) {
976 return new AmsTask(activity, handler, callback) {
977 public void doWork() throws RemoteException {
978 mService.updateCredentials(mResponse, account, authTokenType, activity != null,
Fred Quintana31957f12009-10-21 13:43:10 -0700979 options);
Fred Quintanaa698f422009-04-08 19:14:54 -0700980 }
Fred Quintana33269202009-04-20 16:05:10 -0700981 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -0700982 }
983
Fred Quintana756b7352009-10-21 13:43:10 -0700984 /**
985 * Request that the properties for an authenticator be updated. This is typically done by
986 * returning an intent to an activity that will allow the user to make changes. This request
987 * is processed by the authenticator for the account. If no matching authenticator is
988 * registered in the system then {@link AuthenticatorException} is thrown.
989 * <p>
990 * This call returns immediately but runs asynchronously and the result is accessed via the
991 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
992 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
993 * method asynchronously then they will generally pass in a callback object that will get
994 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
995 * they will generally pass null for the callback and instead call
996 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
997 * which will then block until the request completes.
998 * <p>
Fred Quintana8570f742010-02-18 10:32:54 -0800999 * Do not block the main thread waiting this method's result.
1000 * <p>
1001 * Not allowed from main thread (but allowed from other threads):
1002 * <pre>
1003 * Bundle result = editProperties(accountType, activity, callback, handler).getResult();
1004 * </pre>
1005 * Allowed from main thread:
1006 * <pre>
1007 * editProperties(accountType, activity, new AccountManagerCallback<Bundle>() {
1008 * public void run(AccountManagerFuture<Bundle> future) {
1009 * Bundle result = future.getResult();
1010 * // use result
1011 * }
1012 * }, handler);
1013 * </pre>
1014 * <p>
Fred Quintana756b7352009-10-21 13:43:10 -07001015 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1016 *
1017 * @param accountType The account type of the authenticator whose properties are to be edited.
1018 * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
1019 * the intent will be started with this activity. If activity is null then the result will
1020 * be returned as-is.
1021 * @param callback A callback to invoke when the request completes. If null then
1022 * no callback is invoked.
1023 * @param handler The {@link Handler} to use to invoke the callback. If null then the
1024 * main thread's {@link Handler} is used.
1025 * @return an {@link AccountManagerFuture} that represents the future result of the call.
1026 * The future result is a {@link Bundle} that contains either:
1027 * <ul>
1028 * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
1029 * <li> nothing, returned if the edit completes successfully
1030 * </ul>
1031 * If the user presses "back" then the request will be canceled.
1032 */
1033 public AccountManagerFuture<Bundle> editProperties(final String accountType,
1034 final Activity activity, final AccountManagerCallback<Bundle> callback,
Fred Quintanaa698f422009-04-08 19:14:54 -07001035 final Handler handler) {
1036 return new AmsTask(activity, handler, callback) {
1037 public void doWork() throws RemoteException {
1038 mService.editProperties(mResponse, accountType, activity != null);
1039 }
Fred Quintana33269202009-04-20 16:05:10 -07001040 }.start();
Fred Quintanaa698f422009-04-08 19:14:54 -07001041 }
1042
1043 private void ensureNotOnMainThread() {
1044 final Looper looper = Looper.myLooper();
1045 if (looper != null && looper == mContext.getMainLooper()) {
Fred Quintana53bd2522010-02-05 15:28:12 -08001046 final IllegalStateException exception = new IllegalStateException(
1047 "calling this from your main thread can lead to deadlock");
1048 Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
1049 exception);
Fred Quintana751fdc02010-02-09 14:13:18 -08001050 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO) {
1051 throw exception;
1052 }
Fred Quintana60307342009-03-24 22:48:12 -07001053 }
1054 }
1055
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001056 private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback,
1057 final AccountManagerFuture<Bundle> future) {
Fred Quintanad9d2f112009-04-23 13:36:27 -07001058 handler = handler == null ? mMainHandler : handler;
1059 handler.post(new Runnable() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001060 public void run() {
1061 callback.run(future);
1062 }
1063 });
1064 }
Fred Quintana60307342009-03-24 22:48:12 -07001065
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001066 private void postToHandler(Handler handler, final OnAccountsUpdateListener listener,
Fred Quintanad9d2f112009-04-23 13:36:27 -07001067 final Account[] accounts) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001068 final Account[] accountsCopy = new Account[accounts.length];
1069 // send a copy to make sure that one doesn't
1070 // change what another sees
1071 System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length);
1072 handler = (handler == null) ? mMainHandler : handler;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001073 handler.post(new Runnable() {
1074 public void run() {
Costin Manolacheb6437242009-09-10 16:14:12 -07001075 try {
1076 listener.onAccountsUpdated(accountsCopy);
1077 } catch (SQLException e) {
1078 // Better luck next time. If the problem was disk-full,
1079 // the STORAGE_OK intent will re-trigger the update.
1080 Log.e(TAG, "Can't update accounts", e);
1081 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001082 }
1083 });
1084 }
1085
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001086 private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
Fred Quintanaa698f422009-04-08 19:14:54 -07001087 final IAccountManagerResponse mResponse;
1088 final Handler mHandler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001089 final AccountManagerCallback<Bundle> mCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -07001090 final Activity mActivity;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001091 public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001092 super(new Callable<Bundle>() {
1093 public Bundle call() throws Exception {
1094 throw new IllegalStateException("this should never be called");
1095 }
1096 });
1097
1098 mHandler = handler;
1099 mCallback = callback;
1100 mActivity = activity;
1101 mResponse = new Response();
Fred Quintana33269202009-04-20 16:05:10 -07001102 }
1103
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001104 public final AccountManagerFuture<Bundle> start() {
1105 try {
1106 doWork();
1107 } catch (RemoteException e) {
1108 setException(e);
1109 }
Fred Quintana33269202009-04-20 16:05:10 -07001110 return this;
Fred Quintana60307342009-03-24 22:48:12 -07001111 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001112
1113 public abstract void doWork() throws RemoteException;
1114
1115 private Bundle internalGetResult(Long timeout, TimeUnit unit)
1116 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintana53bd2522010-02-05 15:28:12 -08001117 if (!isDone()) {
1118 ensureNotOnMainThread();
1119 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001120 try {
1121 if (timeout == null) {
1122 return get();
1123 } else {
1124 return get(timeout, unit);
1125 }
1126 } catch (CancellationException e) {
1127 throw new OperationCanceledException();
1128 } catch (TimeoutException e) {
1129 // fall through and cancel
1130 } catch (InterruptedException e) {
1131 // fall through and cancel
1132 } catch (ExecutionException e) {
1133 final Throwable cause = e.getCause();
1134 if (cause instanceof IOException) {
1135 throw (IOException) cause;
1136 } else if (cause instanceof UnsupportedOperationException) {
1137 throw new AuthenticatorException(cause);
1138 } else if (cause instanceof AuthenticatorException) {
1139 throw (AuthenticatorException) cause;
1140 } else if (cause instanceof RuntimeException) {
1141 throw (RuntimeException) cause;
1142 } else if (cause instanceof Error) {
1143 throw (Error) cause;
1144 } else {
1145 throw new IllegalStateException(cause);
1146 }
1147 } finally {
1148 cancel(true /* interrupt if running */);
1149 }
1150 throw new OperationCanceledException();
1151 }
1152
1153 public Bundle getResult()
1154 throws OperationCanceledException, IOException, AuthenticatorException {
1155 return internalGetResult(null, null);
1156 }
1157
1158 public Bundle getResult(long timeout, TimeUnit unit)
1159 throws OperationCanceledException, IOException, AuthenticatorException {
1160 return internalGetResult(timeout, unit);
1161 }
1162
1163 protected void done() {
1164 if (mCallback != null) {
1165 postToHandler(mHandler, mCallback, this);
1166 }
1167 }
1168
1169 /** Handles the responses from the AccountManager */
1170 private class Response extends IAccountManagerResponse.Stub {
1171 public void onResult(Bundle bundle) {
1172 Intent intent = bundle.getParcelable("intent");
1173 if (intent != null && mActivity != null) {
1174 // since the user provided an Activity we will silently start intents
1175 // that we see
1176 mActivity.startActivity(intent);
1177 // leave the Future running to wait for the real response to this request
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001178 } else if (bundle.getBoolean("retry")) {
1179 try {
1180 doWork();
1181 } catch (RemoteException e) {
1182 // this will only happen if the system process is dead, which means
1183 // we will be dying ourselves
1184 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001185 } else {
1186 set(bundle);
1187 }
1188 }
1189
1190 public void onError(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001191 if (code == ERROR_CODE_CANCELED) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001192 // the authenticator indicated that this request was canceled, do so now
1193 cancel(true /* mayInterruptIfRunning */);
1194 return;
1195 }
1196 setException(convertErrorToException(code, message));
1197 }
1198 }
1199
Fred Quintana60307342009-03-24 22:48:12 -07001200 }
1201
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001202 private abstract class BaseFutureTask<T> extends FutureTask<T> {
1203 final public IAccountManagerResponse mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07001204 final Handler mHandler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001205
1206 public BaseFutureTask(Handler handler) {
1207 super(new Callable<T>() {
1208 public T call() throws Exception {
Fred Quintanaa698f422009-04-08 19:14:54 -07001209 throw new IllegalStateException("this should never be called");
1210 }
1211 });
Fred Quintanaa698f422009-04-08 19:14:54 -07001212 mHandler = handler;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001213 mResponse = new Response();
Fred Quintana60307342009-03-24 22:48:12 -07001214 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001215
1216 public abstract void doWork() throws RemoteException;
1217
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001218 public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException;
Fred Quintanaa698f422009-04-08 19:14:54 -07001219
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001220 protected void postRunnableToHandler(Runnable runnable) {
1221 Handler handler = (mHandler == null) ? mMainHandler : mHandler;
1222 handler.post(runnable);
Fred Quintanaa698f422009-04-08 19:14:54 -07001223 }
1224
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001225 protected void startTask() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001226 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001227 doWork();
1228 } catch (RemoteException e) {
1229 setException(e);
Fred Quintanaa698f422009-04-08 19:14:54 -07001230 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001231 }
1232
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001233 protected class Response extends IAccountManagerResponse.Stub {
Fred Quintanaa698f422009-04-08 19:14:54 -07001234 public void onResult(Bundle bundle) {
1235 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001236 T result = bundleToResult(bundle);
1237 if (result == null) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001238 return;
1239 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001240 set(result);
1241 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001242 } catch (ClassCastException e) {
1243 // we will set the exception below
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001244 } catch (AuthenticatorException e) {
1245 // we will set the exception below
Fred Quintanaa698f422009-04-08 19:14:54 -07001246 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001247 onError(ERROR_CODE_INVALID_RESPONSE, "no result in response");
Fred Quintanaa698f422009-04-08 19:14:54 -07001248 }
1249
1250 public void onError(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001251 if (code == ERROR_CODE_CANCELED) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001252 cancel(true /* mayInterruptIfRunning */);
1253 return;
1254 }
1255 setException(convertErrorToException(code, message));
1256 }
1257 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001258 }
1259
1260 private abstract class Future2Task<T>
1261 extends BaseFutureTask<T> implements AccountManagerFuture<T> {
1262 final AccountManagerCallback<T> mCallback;
1263 public Future2Task(Handler handler, AccountManagerCallback<T> callback) {
1264 super(handler);
1265 mCallback = callback;
1266 }
1267
1268 protected void done() {
1269 if (mCallback != null) {
1270 postRunnableToHandler(new Runnable() {
1271 public void run() {
1272 mCallback.run(Future2Task.this);
1273 }
1274 });
1275 }
1276 }
1277
1278 public Future2Task<T> start() {
1279 startTask();
1280 return this;
1281 }
1282
1283 private T internalGetResult(Long timeout, TimeUnit unit)
1284 throws OperationCanceledException, IOException, AuthenticatorException {
Fred Quintana53bd2522010-02-05 15:28:12 -08001285 if (!isDone()) {
1286 ensureNotOnMainThread();
1287 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001288 try {
1289 if (timeout == null) {
1290 return get();
1291 } else {
1292 return get(timeout, unit);
1293 }
1294 } catch (InterruptedException e) {
1295 // fall through and cancel
1296 } catch (TimeoutException e) {
1297 // fall through and cancel
1298 } catch (CancellationException e) {
1299 // fall through and cancel
1300 } catch (ExecutionException e) {
1301 final Throwable cause = e.getCause();
1302 if (cause instanceof IOException) {
1303 throw (IOException) cause;
1304 } else if (cause instanceof UnsupportedOperationException) {
1305 throw new AuthenticatorException(cause);
1306 } else if (cause instanceof AuthenticatorException) {
1307 throw (AuthenticatorException) cause;
1308 } else if (cause instanceof RuntimeException) {
1309 throw (RuntimeException) cause;
1310 } else if (cause instanceof Error) {
1311 throw (Error) cause;
1312 } else {
1313 throw new IllegalStateException(cause);
1314 }
1315 } finally {
1316 cancel(true /* interrupt if running */);
1317 }
1318 throw new OperationCanceledException();
1319 }
1320
1321 public T getResult()
1322 throws OperationCanceledException, IOException, AuthenticatorException {
1323 return internalGetResult(null, null);
1324 }
1325
1326 public T getResult(long timeout, TimeUnit unit)
1327 throws OperationCanceledException, IOException, AuthenticatorException {
1328 return internalGetResult(timeout, unit);
1329 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001330
Fred Quintana60307342009-03-24 22:48:12 -07001331 }
1332
Fred Quintanaa698f422009-04-08 19:14:54 -07001333 private Exception convertErrorToException(int code, String message) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001334 if (code == ERROR_CODE_NETWORK_ERROR) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001335 return new IOException(message);
Fred Quintana60307342009-03-24 22:48:12 -07001336 }
Fred Quintana60307342009-03-24 22:48:12 -07001337
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001338 if (code == ERROR_CODE_UNSUPPORTED_OPERATION) {
Fred Quintana33269202009-04-20 16:05:10 -07001339 return new UnsupportedOperationException(message);
Fred Quintana60307342009-03-24 22:48:12 -07001340 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001341
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001342 if (code == ERROR_CODE_INVALID_RESPONSE) {
Fred Quintana33269202009-04-20 16:05:10 -07001343 return new AuthenticatorException(message);
Fred Quintanaa698f422009-04-08 19:14:54 -07001344 }
1345
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001346 if (code == ERROR_CODE_BAD_ARGUMENTS) {
Fred Quintana33269202009-04-20 16:05:10 -07001347 return new IllegalArgumentException(message);
1348 }
1349
1350 return new AuthenticatorException(message);
1351 }
1352
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001353 private class GetAuthTokenByTypeAndFeaturesTask
1354 extends AmsTask implements AccountManagerCallback<Bundle> {
Fred Quintana33269202009-04-20 16:05:10 -07001355 GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
1356 final String[] features, Activity activityForPrompting,
1357 final Bundle addAccountOptions, final Bundle loginOptions,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001358 AccountManagerCallback<Bundle> callback, Handler handler) {
Fred Quintana33269202009-04-20 16:05:10 -07001359 super(activityForPrompting, handler, callback);
1360 if (accountType == null) throw new IllegalArgumentException("account type is null");
1361 mAccountType = accountType;
1362 mAuthTokenType = authTokenType;
1363 mFeatures = features;
1364 mAddAccountOptions = addAccountOptions;
1365 mLoginOptions = loginOptions;
1366 mMyCallback = this;
1367 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001368 volatile AccountManagerFuture<Bundle> mFuture = null;
Fred Quintana33269202009-04-20 16:05:10 -07001369 final String mAccountType;
1370 final String mAuthTokenType;
1371 final String[] mFeatures;
1372 final Bundle mAddAccountOptions;
1373 final Bundle mLoginOptions;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001374 final AccountManagerCallback<Bundle> mMyCallback;
Fred Quintana33269202009-04-20 16:05:10 -07001375
1376 public void doWork() throws RemoteException {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001377 getAccountsByTypeAndFeatures(mAccountType, mFeatures,
1378 new AccountManagerCallback<Account[]>() {
1379 public void run(AccountManagerFuture<Account[]> future) {
1380 Account[] accounts;
Fred Quintana33269202009-04-20 16:05:10 -07001381 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001382 accounts = future.getResult();
1383 } catch (OperationCanceledException e) {
1384 setException(e);
1385 return;
1386 } catch (IOException e) {
1387 setException(e);
1388 return;
1389 } catch (AuthenticatorException e) {
1390 setException(e);
1391 return;
Fred Quintana33269202009-04-20 16:05:10 -07001392 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001393
1394 if (accounts.length == 0) {
1395 if (mActivity != null) {
1396 // no accounts, add one now. pretend that the user directly
1397 // made this request
1398 mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
1399 mAddAccountOptions, mActivity, mMyCallback, mHandler);
1400 } else {
1401 // send result since we can't prompt to add an account
1402 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001403 result.putString(KEY_ACCOUNT_NAME, null);
1404 result.putString(KEY_ACCOUNT_TYPE, null);
1405 result.putString(KEY_AUTHTOKEN, null);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001406 try {
1407 mResponse.onResult(result);
1408 } catch (RemoteException e) {
1409 // this will never happen
1410 }
1411 // we are done
1412 }
1413 } else if (accounts.length == 1) {
1414 // have a single account, return an authtoken for it
1415 if (mActivity == null) {
1416 mFuture = getAuthToken(accounts[0], mAuthTokenType,
1417 false /* notifyAuthFailure */, mMyCallback, mHandler);
1418 } else {
1419 mFuture = getAuthToken(accounts[0],
1420 mAuthTokenType, mLoginOptions,
Fred Quintana33269202009-04-20 16:05:10 -07001421 mActivity, mMyCallback, mHandler);
1422 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001423 } else {
1424 if (mActivity != null) {
1425 IAccountManagerResponse chooseResponse =
1426 new IAccountManagerResponse.Stub() {
1427 public void onResult(Bundle value) throws RemoteException {
1428 Account account = new Account(
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001429 value.getString(KEY_ACCOUNT_NAME),
1430 value.getString(KEY_ACCOUNT_TYPE));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001431 mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
1432 mActivity, mMyCallback, mHandler);
1433 }
Fred Quintana33269202009-04-20 16:05:10 -07001434
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001435 public void onError(int errorCode, String errorMessage)
1436 throws RemoteException {
1437 mResponse.onError(errorCode, errorMessage);
1438 }
1439 };
1440 // have many accounts, launch the chooser
1441 Intent intent = new Intent();
1442 intent.setClassName("android",
1443 "android.accounts.ChooseAccountActivity");
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001444 intent.putExtra(KEY_ACCOUNTS, accounts);
1445 intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001446 new AccountManagerResponse(chooseResponse));
1447 mActivity.startActivity(intent);
1448 // the result will arrive via the IAccountManagerResponse
1449 } else {
1450 // send result since we can't prompt to select an account
1451 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001452 result.putString(KEY_ACCOUNTS, null);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001453 try {
1454 mResponse.onResult(result);
1455 } catch (RemoteException e) {
1456 // this will never happen
1457 }
1458 // we are done
Fred Quintana33269202009-04-20 16:05:10 -07001459 }
Fred Quintana33269202009-04-20 16:05:10 -07001460 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001461 }}, mHandler);
Fred Quintana33269202009-04-20 16:05:10 -07001462 }
1463
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001464 public void run(AccountManagerFuture<Bundle> future) {
Fred Quintana33269202009-04-20 16:05:10 -07001465 try {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001466 set(future.getResult());
1467 } catch (OperationCanceledException e) {
1468 cancel(true /* mayInterruptIfRUnning */);
1469 } catch (IOException e) {
1470 setException(e);
1471 } catch (AuthenticatorException e) {
1472 setException(e);
Fred Quintana33269202009-04-20 16:05:10 -07001473 }
1474 }
1475 }
1476
Fred Quintana756b7352009-10-21 13:43:10 -07001477 /**
1478 * Convenience method that combines the functionality of {@link #getAccountsByTypeAndFeatures},
1479 * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)},
1480 * and {@link #addAccount}. It first gets the list of accounts that match accountType and the
1481 * feature set. If there are none then {@link #addAccount} is invoked with the authTokenType
1482 * feature set, and addAccountOptions. If there is exactly one then
1483 * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)} is
1484 * called with that account. If there are more than one then a chooser activity is launched
1485 * to prompt the user to select one of them and then the authtoken is retrieved for it,
1486 * <p>
1487 * This call returns immediately but runs asynchronously and the result is accessed via the
1488 * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
1489 * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
1490 * method asynchronously then they will generally pass in a callback object that will get
1491 * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
1492 * they will generally pass null for the callback and instead call
1493 * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
1494 * which will then block until the request completes.
1495 * <p>
1496 * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
1497 *
1498 * @param accountType the accountType to query; this must be non-null
1499 * @param authTokenType the type of authtoken to retrieve; this must be non-null
1500 * @param features a filter for the accounts. See {@link #getAccountsByTypeAndFeatures}.
1501 * @param activityForPrompting The activity used to start any account management
1502 * activities that are required to fulfill this request. This may be null.
1503 * @param addAccountOptions authenticator-specific options used if an account needs to be added
Fred Quintana31957f12009-10-21 13:43:10 -07001504 * @param getAuthTokenOptions authenticator-specific options passed to getAuthToken
Fred Quintana756b7352009-10-21 13:43:10 -07001505 * @param callback A callback to invoke when the request completes. If null then
1506 * no callback is invoked.
1507 * @param handler The {@link Handler} to use to invoke the callback. If null then the
1508 * main thread's {@link Handler} is used.
1509 * @return an {@link AccountManagerFuture} that represents the future result of the call.
1510 * The future result is a {@link Bundle} that contains either:
1511 * <ul>
1512 * <li> {@link #KEY_INTENT}, if no activity is supplied yet an activity needs to launched to
1513 * fulfill the request.
1514 * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN} if the
1515 * request completes successfully.
1516 * </ul>
1517 * If the user presses "back" then the request will be canceled.
1518 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001519 public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
Fred Quintana33269202009-04-20 16:05:10 -07001520 final String accountType, final String authTokenType, final String[] features,
1521 final Activity activityForPrompting, final Bundle addAccountOptions,
Fred Quintana31957f12009-10-21 13:43:10 -07001522 final Bundle getAuthTokenOptions,
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001523 final AccountManagerCallback<Bundle> callback, final Handler handler) {
Fred Quintana33269202009-04-20 16:05:10 -07001524 if (accountType == null) throw new IllegalArgumentException("account type is null");
1525 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001526 final GetAuthTokenByTypeAndFeaturesTask task =
1527 new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
Fred Quintana31957f12009-10-21 13:43:10 -07001528 activityForPrompting, addAccountOptions, getAuthTokenOptions, callback, handler);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001529 task.start();
1530 return task;
Fred Quintana60307342009-03-24 22:48:12 -07001531 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001532
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001533 private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
Fred Quintanad9d2f112009-04-23 13:36:27 -07001534 Maps.newHashMap();
1535
Fred Quintanad9d2f112009-04-23 13:36:27 -07001536 /**
1537 * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
1538 * so that it can read the updated list of accounts and send them to the listener
1539 * in mAccountsUpdatedListeners.
1540 */
1541 private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() {
1542 public void onReceive(final Context context, final Intent intent) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001543 final Account[] accounts = getAccounts();
1544 // send the result to the listeners
1545 synchronized (mAccountsUpdatedListeners) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001546 for (Map.Entry<OnAccountsUpdateListener, Handler> entry :
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001547 mAccountsUpdatedListeners.entrySet()) {
1548 postToHandler(entry.getValue(), entry.getKey(), accounts);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001549 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001550 }
Fred Quintanad9d2f112009-04-23 13:36:27 -07001551 }
1552 };
1553
1554 /**
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001555 * Add a {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}.
Fred Quintanad9d2f112009-04-23 13:36:27 -07001556 * The listener is guaranteed to be invoked on the thread of the Handler that is passed
1557 * in or the main thread's Handler if handler is null.
Fred Quintanae00a3112009-09-22 15:13:30 -07001558 * <p>
1559 * You must remove this listener before the context that was used to retrieve this
1560 * {@link AccountManager} instance goes away. This generally means when the Activity
1561 * or Service you are running is stopped.
Fred Quintanad9d2f112009-04-23 13:36:27 -07001562 * @param listener the listener to add
1563 * @param handler the Handler whose thread will be used to invoke the listener. If null
1564 * the AccountManager context's main thread will be used.
1565 * @param updateImmediately if true then the listener will be invoked as a result of this
1566 * call.
1567 * @throws IllegalArgumentException if listener is null
1568 * @throws IllegalStateException if listener was already added
1569 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001570 public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
Fred Quintanad9d2f112009-04-23 13:36:27 -07001571 Handler handler, boolean updateImmediately) {
1572 if (listener == null) {
1573 throw new IllegalArgumentException("the listener is null");
1574 }
1575 synchronized (mAccountsUpdatedListeners) {
1576 if (mAccountsUpdatedListeners.containsKey(listener)) {
1577 throw new IllegalStateException("this listener is already added");
1578 }
1579 final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
1580
1581 mAccountsUpdatedListeners.put(listener, handler);
1582
1583 if (wasEmpty) {
1584 // Register a broadcast receiver to monitor account changes
1585 IntentFilter intentFilter = new IntentFilter();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001586 intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
Costin Manolacheb6437242009-09-10 16:14:12 -07001587 // To recover from disk-full.
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001588 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001589 mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
1590 }
1591 }
1592
1593 if (updateImmediately) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001594 postToHandler(handler, listener, getAccounts());
Fred Quintanad9d2f112009-04-23 13:36:27 -07001595 }
1596 }
1597
1598 /**
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001599 * Remove an {@link OnAccountsUpdateListener} that was previously registered with
Fred Quintanad9d2f112009-04-23 13:36:27 -07001600 * {@link #addOnAccountsUpdatedListener}.
1601 * @param listener the listener to remove
1602 * @throws IllegalArgumentException if listener is null
1603 * @throws IllegalStateException if listener was not already added
1604 */
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001605 public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
Fred Quintanad9d2f112009-04-23 13:36:27 -07001606 if (listener == null) {
Costin Manolache88a211b2009-10-29 11:30:11 -07001607 Log.e(TAG, "Missing listener");
1608 return;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001609 }
1610 synchronized (mAccountsUpdatedListeners) {
Bryan Mawhinney5be61f52009-09-24 14:50:25 +01001611 if (!mAccountsUpdatedListeners.containsKey(listener)) {
Costin Manolache88a211b2009-10-29 11:30:11 -07001612 Log.e(TAG, "Listener was not previously added");
1613 return;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001614 }
Bryan Mawhinney5be61f52009-09-24 14:50:25 +01001615 mAccountsUpdatedListeners.remove(listener);
Fred Quintanad9d2f112009-04-23 13:36:27 -07001616 if (mAccountsUpdatedListeners.isEmpty()) {
1617 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
1618 }
1619 }
1620 }
Fred Quintana60307342009-03-24 22:48:12 -07001621}