blob: c60f15da11a5cb749a84cb3aed8f11af6c91ac85 [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 Quintanaa698f422009-04-08 19:14:54 -070022import android.os.Bundle;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.RemoteException;
Fred Quintana60307342009-03-24 22:48:12 -070026
Fred Quintanaa698f422009-04-08 19:14:54 -070027import java.io.IOException;
28import java.util.concurrent.Callable;
29import java.util.concurrent.CancellationException;
30import java.util.concurrent.ExecutionException;
31import java.util.concurrent.FutureTask;
32import java.util.concurrent.TimeoutException;
33import java.util.concurrent.TimeUnit;
Fred Quintana60307342009-03-24 22:48:12 -070034
35/**
36 * A class that helps with interactions with the {@link IAccountManager} interface. It provides
37 * methods to allow for account, password, and authtoken management for all accounts on the
38 * device. Some of these calls are implemented with the help of the corresponding
39 * {@link IAccountAuthenticator} services. One accesses the {@link AccountManager} by calling:
Fred Quintanaa698f422009-04-08 19:14:54 -070040 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana60307342009-03-24 22:48:12 -070041 *
42 * <p>
Fred Quintanaa698f422009-04-08 19:14:54 -070043 * TODO(fredq) this interface is still in flux
Fred Quintana60307342009-03-24 22:48:12 -070044 */
45public class AccountManager {
46 private static final String TAG = "AccountManager";
47
48 private final Context mContext;
49 private final IAccountManager mService;
50
51 public AccountManager(Context context, IAccountManager service) {
52 mContext = context;
53 mService = service;
54 }
55
Fred Quintanaa698f422009-04-08 19:14:54 -070056 public static AccountManager get(Context context) {
57 return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
58 }
59
60 public String blockingGetPassword(Account account) {
61 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -070062 try {
63 return mService.getPassword(account);
64 } catch (RemoteException e) {
65 // if this happens the entire runtime will restart
66 throw new RuntimeException(e);
67 }
68 }
69
Fred Quintanaa698f422009-04-08 19:14:54 -070070 public Future1<String> getPassword(final Future1Callback<String> callback,
71 final Account account, final Handler handler) {
72 return startAsFuture(callback, handler, new Callable<String>() {
73 public String call() throws Exception {
74 return blockingGetPassword(account);
75 }
76 });
77 }
78
79 public String blockingGetUserData(Account account, String key) {
80 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -070081 try {
82 return mService.getUserData(account, key);
83 } catch (RemoteException e) {
84 // if this happens the entire runtime will restart
85 throw new RuntimeException(e);
86 }
87 }
88
Fred Quintanaa698f422009-04-08 19:14:54 -070089 public Future1<String> getUserData(Future1Callback<String> callback,
90 final Account account, final String key, Handler handler) {
91 return startAsFuture(callback, handler, new Callable<String>() {
92 public String call() throws Exception {
93 return blockingGetUserData(account, key);
94 }
95 });
96 }
97
98 public String[] blockingGetAuthenticatorTypes() {
99 ensureNotOnMainThread();
100 try {
101 return mService.getAuthenticatorTypes();
102 } catch (RemoteException e) {
103 // if this happens the entire runtime will restart
104 throw new RuntimeException(e);
105 }
106 }
107
108 public Future1<String[]> getAuthenticatorTypes(Future1Callback<String[]> callback,
109 Handler handler) {
110 return startAsFuture(callback, handler, new Callable<String[]>() {
111 public String[] call() throws Exception {
112 return blockingGetAuthenticatorTypes();
113 }
114 });
115 }
116
Fred Quintana60307342009-03-24 22:48:12 -0700117 public Account[] blockingGetAccounts() {
Fred Quintanaa698f422009-04-08 19:14:54 -0700118 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700119 try {
120 return mService.getAccounts();
121 } catch (RemoteException e) {
122 // if this happens the entire runtime will restart
123 throw new RuntimeException(e);
124 }
125 }
126
Fred Quintana60307342009-03-24 22:48:12 -0700127 public Account[] blockingGetAccountsByType(String accountType) {
Fred Quintanaa698f422009-04-08 19:14:54 -0700128 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700129 try {
130 return mService.getAccountsByType(accountType);
131 } catch (RemoteException e) {
132 // if this happens the entire runtime will restart
133 throw new RuntimeException(e);
134 }
135 }
136
Fred Quintanaa698f422009-04-08 19:14:54 -0700137 public Future1<Account[]> getAccounts(Future1Callback<Account[]> callback, Handler handler) {
138 return startAsFuture(callback, handler, new Callable<Account[]>() {
139 public Account[] call() throws Exception {
140 return blockingGetAccounts();
141 }
142 });
143 }
144
145 public Future1<Account[]> getAccountsByType(Future1Callback<Account[]> callback,
146 final String type, Handler handler) {
147 return startAsFuture(callback, handler, new Callable<Account[]>() {
148 public Account[] call() throws Exception {
149 return blockingGetAccountsByType(type);
150 }
151 });
152 }
153
154 public boolean blockingAddAccountExplicitly(Account account, String password, Bundle extras) {
155 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700156 try {
157 return mService.addAccount(account, password, extras);
158 } catch (RemoteException e) {
159 // if this happens the entire runtime will restart
160 throw new RuntimeException(e);
161 }
162 }
163
Fred Quintanaa698f422009-04-08 19:14:54 -0700164 public Future1<Boolean> addAccountExplicitly(final Future1Callback<Boolean> callback,
165 final Account account, final String password, final Bundle extras,
166 final Handler handler) {
167 return startAsFuture(callback, handler, new Callable<Boolean>() {
168 public Boolean call() throws Exception {
169 return blockingAddAccountExplicitly(account, password, extras);
170 }
171 });
172 }
173
174 public void blockingRemoveAccount(Account account) {
175 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700176 try {
177 mService.removeAccount(account);
178 } catch (RemoteException e) {
179 // if this happens the entire runtime will restart
180 }
181 }
182
Fred Quintanaa698f422009-04-08 19:14:54 -0700183 public Future1<Void> removeAccount(Future1Callback<Void> callback, final Account account,
184 final Handler handler) {
185 return startAsFuture(callback, handler, new Callable<Void>() {
186 public Void call() throws Exception {
187 blockingRemoveAccount(account);
188 return null;
189 }
190 });
191 }
192
193 public void blockingInvalidateAuthToken(String accountType, String authToken) {
194 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700195 try {
196 mService.invalidateAuthToken(accountType, authToken);
197 } catch (RemoteException e) {
198 // if this happens the entire runtime will restart
199 }
200 }
201
Fred Quintanaa698f422009-04-08 19:14:54 -0700202 public Future1<Void> invalidateAuthToken(Future1Callback<Void> callback,
203 final String accountType, final String authToken, final Handler handler) {
204 return startAsFuture(callback, handler, new Callable<Void>() {
205 public Void call() throws Exception {
206 blockingInvalidateAuthToken(accountType, authToken);
207 return null;
208 }
209 });
210 }
211
212 public String blockingPeekAuthToken(Account account, String authTokenType) {
213 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700214 try {
215 return mService.peekAuthToken(account, authTokenType);
216 } catch (RemoteException e) {
217 // if this happens the entire runtime will restart
218 throw new RuntimeException(e);
219 }
220 }
221
Fred Quintanaa698f422009-04-08 19:14:54 -0700222 public Future1<String> peekAuthToken(Future1Callback<String> callback,
223 final Account account, final String authTokenType, final Handler handler) {
224 return startAsFuture(callback, handler, new Callable<String>() {
225 public String call() throws Exception {
226 return blockingPeekAuthToken(account, authTokenType);
227 }
228 });
229 }
230
231 public void blockingSetPassword(Account account, String password) {
232 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700233 try {
234 mService.setPassword(account, password);
235 } catch (RemoteException e) {
236 // if this happens the entire runtime will restart
237 }
238 }
239
Fred Quintanaa698f422009-04-08 19:14:54 -0700240 public Future1<Void> setPassword(Future1Callback<Void> callback,
241 final Account account, final String password, final Handler handler) {
242 return startAsFuture(callback, handler, new Callable<Void>() {
243 public Void call() throws Exception {
244 blockingSetPassword(account, password);
245 return null;
246 }
247 });
248 }
249
250 public void blockingClearPassword(Account account) {
251 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700252 try {
253 mService.clearPassword(account);
254 } catch (RemoteException e) {
255 // if this happens the entire runtime will restart
256 }
257 }
258
Fred Quintanaa698f422009-04-08 19:14:54 -0700259 public Future1<Void> clearPassword(final Future1Callback<Void> callback, final Account account,
260 final Handler handler) {
261 return startAsFuture(callback, handler, new Callable<Void>() {
262 public Void call() throws Exception {
263 blockingClearPassword(account);
264 return null;
265 }
266 });
267 }
268
269 public void blockingSetUserData(Account account, String key, String value) {
270 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700271 try {
272 mService.setUserData(account, key, value);
273 } catch (RemoteException e) {
274 // if this happens the entire runtime will restart
275 }
276 }
277
Fred Quintanaa698f422009-04-08 19:14:54 -0700278 public Future1<Void> setUserData(Future1Callback<Void> callback,
279 final Account account, final String key, final String value, final Handler handler) {
280 return startAsFuture(callback, handler, new Callable<Void>() {
281 public Void call() throws Exception {
282 blockingSetUserData(account, key, value);
283 return null;
284 }
285 });
Fred Quintana60307342009-03-24 22:48:12 -0700286 }
287
Fred Quintanaa698f422009-04-08 19:14:54 -0700288 public void blockingSetAuthToken(Account account, String authTokenType, String authToken) {
289 ensureNotOnMainThread();
Fred Quintana60307342009-03-24 22:48:12 -0700290 try {
291 mService.setAuthToken(account, authTokenType, authToken);
292 } catch (RemoteException e) {
293 // if this happens the entire runtime will restart
294 }
295 }
296
Fred Quintanaa698f422009-04-08 19:14:54 -0700297 public Future1<Void> setAuthToken(Future1Callback<Void> callback,
298 final Account account, final String authTokenType, final String authToken,
299 final Handler handler) {
300 return startAsFuture(callback, handler, new Callable<Void>() {
301 public Void call() throws Exception {
302 blockingSetAuthToken(account, authTokenType, authToken);
303 return null;
304 }
305 });
306 }
307
308 public String blockingGetAuthToken(Account account, String authTokenType,
309 boolean notifyAuthFailure)
310 throws OperationCanceledException, IOException, AuthenticatorException {
311 ensureNotOnMainThread();
312 Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
313 null /* handler */).getResult();
314 return bundle.getString(Constants.AUTHTOKEN_KEY);
315 }
316
317 /**
318 * Request the auth token for this account/authTokenType. If this succeeds then the
319 * auth token will then be passed to the activity. If this results in an authentication
320 * failure then a login intent will be returned that can be invoked to prompt the user to
321 * update their credentials. This login activity will return the auth token to the calling
322 * activity. If activity is null then the login intent will not be invoked.
323 *
324 * @param account the account whose auth token should be retrieved
325 * @param authTokenType the auth token type that should be retrieved
326 * @param loginOptions
327 * @param activity the activity to launch the login intent, if necessary, and to which
328 */
329 public Future2 getAuthToken(
330 final Account account, final String authTokenType, final Bundle loginOptions,
331 final Activity activity, Future2Callback callback, Handler handler) {
332 if (activity == null) throw new IllegalArgumentException("activity is null");
333 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
334 return new AmsTask(activity, handler, callback) {
335 public void doWork() throws RemoteException {
336 mService.getAuthToken(mResponse, account, authTokenType,
337 false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
338 loginOptions);
339 }
340 };
341 }
342
343 public Future2 getAuthToken(
344 final Account account, final String authTokenType, final boolean notifyAuthFailure,
345 Future2Callback callback, Handler handler) {
346 if (account == null) throw new IllegalArgumentException("account is null");
347 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
348 return new AmsTask(null, handler, callback) {
349 public void doWork() throws RemoteException {
350 mService.getAuthToken(mResponse, account, authTokenType,
351 notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
352 }
353 };
354 }
355
356 public Future2 addAccount(final String accountType,
357 final String authTokenType, final Bundle addAccountOptions,
358 final Activity activity, Future2Callback callback, Handler handler) {
359 return new AmsTask(activity, handler, callback) {
360 public void doWork() throws RemoteException {
361 mService.addAcount(mResponse, accountType, authTokenType,
362 activity != null, addAccountOptions);
363 }
364 };
365 }
366
367 /** @deprecated use {@link #confirmCredentials} instead */
368 public Future1<Boolean> confirmPassword(final Account account, final String password,
369 Future1Callback<Boolean> callback, Handler handler) {
370 return new AMSTaskBoolean(handler, callback) {
371 public void doWork() throws RemoteException {
372 mService.confirmPassword(response, account, password);
373 }
374 };
375 }
376
377 public Future2 confirmCredentials(final Account account, final Activity activity,
378 final Future2Callback callback,
379 final Handler handler) {
380 return new AmsTask(activity, handler, callback) {
381 public void doWork() throws RemoteException {
382 mService.confirmCredentials(mResponse, account, activity != null);
383 }
384 };
385 }
386
387 public Future2 updateCredentials(final Account account, final String authTokenType,
388 final Bundle loginOptions, final Activity activity,
389 final Future2Callback callback,
390 final Handler handler) {
391 return new AmsTask(activity, handler, callback) {
392 public void doWork() throws RemoteException {
393 mService.updateCredentials(mResponse, account, authTokenType, activity != null,
394 loginOptions);
395 }
396 };
397 }
398
399 public Future2 editProperties(final String accountType, final Activity activity,
400 final Future2Callback callback,
401 final Handler handler) {
402 return new AmsTask(activity, handler, callback) {
403 public void doWork() throws RemoteException {
404 mService.editProperties(mResponse, accountType, activity != null);
405 }
406 };
407 }
408
409 private void ensureNotOnMainThread() {
410 final Looper looper = Looper.myLooper();
411 if (looper != null && looper == mContext.getMainLooper()) {
412 // We really want to throw an exception here, but GTalkService exercises this
413 // path quite a bit and needs some serious rewrite in order to work properly.
414 //noinspection ThrowableInstanceNeverThrow
415// Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
416// new Exception());
417 // TODO(fredq) remove the log and throw this exception when the callers are fixed
418// throw new IllegalStateException(
419// "calling this from your main thread can lead to deadlock");
Fred Quintana60307342009-03-24 22:48:12 -0700420 }
421 }
422
Fred Quintanaa698f422009-04-08 19:14:54 -0700423 private void postToHandler(Handler handler, final Future2Callback callback,
424 final Future2 future) {
425 if (handler == null) {
426 handler = new Handler(mContext.getMainLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700427 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700428 final Handler innerHandler = handler;
429 innerHandler.post(new Runnable() {
430 public void run() {
431 callback.run(future);
432 }
433 });
434 }
Fred Quintana60307342009-03-24 22:48:12 -0700435
Fred Quintanaa698f422009-04-08 19:14:54 -0700436 private <V> void postToHandler(Handler handler, final Future1Callback<V> callback,
437 final Future1<V> future) {
438 if (handler == null) {
439 handler = new Handler(mContext.getMainLooper());
440 }
441 final Handler innerHandler = handler;
442 innerHandler.post(new Runnable() {
443 public void run() {
444 callback.run(future);
445 }
446 });
447 }
Fred Quintana60307342009-03-24 22:48:12 -0700448
Fred Quintanaa698f422009-04-08 19:14:54 -0700449 private <V> Future1<V> startAsFuture(Future1Callback<V> callback, Handler handler,
450 Callable<V> callable) {
451 final FutureTaskWithCallback<V> task =
452 new FutureTaskWithCallback<V>(callback, callable, handler);
453 new Thread(task).start();
454 return task;
455 }
Fred Quintana60307342009-03-24 22:48:12 -0700456
Fred Quintanaa698f422009-04-08 19:14:54 -0700457 private class FutureTaskWithCallback<V> extends FutureTask<V> implements Future1<V> {
458 final Future1Callback<V> mCallback;
459 final Handler mHandler;
Fred Quintana60307342009-03-24 22:48:12 -0700460
Fred Quintanaa698f422009-04-08 19:14:54 -0700461 public FutureTaskWithCallback(Future1Callback<V> callback, Callable<V> callable,
462 Handler handler) {
463 super(callable);
464 mCallback = callback;
465 mHandler = handler;
466 }
467
468 protected void done() {
469 if (mCallback != null) {
470 postToHandler(mHandler, mCallback, this);
Fred Quintana60307342009-03-24 22:48:12 -0700471 }
472 }
Fred Quintana60307342009-03-24 22:48:12 -0700473
Fred Quintanaa698f422009-04-08 19:14:54 -0700474 public V internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException {
475 try {
476 if (timeout == null) {
477 return get();
478 } else {
479 return get(timeout, unit);
480 }
481 } catch (InterruptedException e) {
482 // we will cancel the task below
483 } catch (CancellationException e) {
484 // we will cancel the task below
485 } catch (TimeoutException e) {
486 // we will cancel the task below
487 } catch (ExecutionException e) {
488 // this should never happen
489 throw new IllegalStateException(e.getCause());
490 } finally {
491 cancel(true /* interruptIfRunning */);
492 }
493 throw new OperationCanceledException();
494 }
495
496 public V getResult() throws OperationCanceledException {
497 return internalGetResult(null, null);
498 }
499
500 public V getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
501 return internalGetResult(null, null);
Fred Quintana60307342009-03-24 22:48:12 -0700502 }
503 }
504
Fred Quintanaa698f422009-04-08 19:14:54 -0700505 public abstract class AmsTask extends FutureTask<Bundle> implements Future2 {
506 final IAccountManagerResponse mResponse;
507 final Handler mHandler;
508 final Future2Callback mCallback;
509 final Activity mActivity;
510 public AmsTask(Activity activity, Handler handler, Future2Callback callback) {
511 super(new Callable<Bundle>() {
512 public Bundle call() throws Exception {
513 throw new IllegalStateException("this should never be called");
514 }
515 });
516
517 mHandler = handler;
518 mCallback = callback;
519 mActivity = activity;
520 mResponse = new Response();
521
522 new Thread(new Runnable() {
523 public void run() {
524 try {
525 doWork();
526 } catch (RemoteException e) {
527 // never happens
528 }
529 }
530 }).start();
Fred Quintana60307342009-03-24 22:48:12 -0700531 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700532
533 public abstract void doWork() throws RemoteException;
534
535 private Bundle internalGetResult(Long timeout, TimeUnit unit)
536 throws OperationCanceledException, IOException, AuthenticatorException {
537 try {
538 if (timeout == null) {
539 return get();
540 } else {
541 return get(timeout, unit);
542 }
543 } catch (CancellationException e) {
544 throw new OperationCanceledException();
545 } catch (TimeoutException e) {
546 // fall through and cancel
547 } catch (InterruptedException e) {
548 // fall through and cancel
549 } catch (ExecutionException e) {
550 final Throwable cause = e.getCause();
551 if (cause instanceof IOException) {
552 throw (IOException) cause;
553 } else if (cause instanceof UnsupportedOperationException) {
554 throw new AuthenticatorException(cause);
555 } else if (cause instanceof AuthenticatorException) {
556 throw (AuthenticatorException) cause;
557 } else if (cause instanceof RuntimeException) {
558 throw (RuntimeException) cause;
559 } else if (cause instanceof Error) {
560 throw (Error) cause;
561 } else {
562 throw new IllegalStateException(cause);
563 }
564 } finally {
565 cancel(true /* interrupt if running */);
566 }
567 throw new OperationCanceledException();
568 }
569
570 public Bundle getResult()
571 throws OperationCanceledException, IOException, AuthenticatorException {
572 return internalGetResult(null, null);
573 }
574
575 public Bundle getResult(long timeout, TimeUnit unit)
576 throws OperationCanceledException, IOException, AuthenticatorException {
577 return internalGetResult(timeout, unit);
578 }
579
580 protected void done() {
581 if (mCallback != null) {
582 postToHandler(mHandler, mCallback, this);
583 }
584 }
585
586 /** Handles the responses from the AccountManager */
587 private class Response extends IAccountManagerResponse.Stub {
588 public void onResult(Bundle bundle) {
589 Intent intent = bundle.getParcelable("intent");
590 if (intent != null && mActivity != null) {
591 // since the user provided an Activity we will silently start intents
592 // that we see
593 mActivity.startActivity(intent);
594 // leave the Future running to wait for the real response to this request
595 } else {
596 set(bundle);
597 }
598 }
599
600 public void onError(int code, String message) {
601 if (code == Constants.ERROR_CODE_CANCELED) {
602 // the authenticator indicated that this request was canceled, do so now
603 cancel(true /* mayInterruptIfRunning */);
604 return;
605 }
606 setException(convertErrorToException(code, message));
607 }
608 }
609
Fred Quintana60307342009-03-24 22:48:12 -0700610 }
611
Fred Quintanaa698f422009-04-08 19:14:54 -0700612 public abstract class AMSTaskBoolean extends FutureTask<Boolean> implements Future1<Boolean> {
613 final IAccountManagerResponse response;
614 final Handler mHandler;
615 final Future1Callback<Boolean> mCallback;
616 public AMSTaskBoolean(Handler handler, Future1Callback<Boolean> callback) {
617 super(new Callable<Boolean>() {
618 public Boolean call() throws Exception {
619 throw new IllegalStateException("this should never be called");
620 }
621 });
622
623 mHandler = handler;
624 mCallback = callback;
625 response = new Response();
626
627 new Thread(new Runnable() {
628 public void run() {
629 try {
630 doWork();
631 } catch (RemoteException e) {
632 // never happens
633 }
634 }
635 }).start();
Fred Quintana60307342009-03-24 22:48:12 -0700636 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700637
638 public abstract void doWork() throws RemoteException;
639
640
641 protected void done() {
642 if (mCallback != null) {
643 postToHandler(mHandler, mCallback, this);
644 }
645 }
646
647 private Boolean internalGetResult(Long timeout, TimeUnit unit) {
648 try {
649 if (timeout == null) {
650 return get();
651 } else {
652 return get(timeout, unit);
653 }
654 } catch (InterruptedException e) {
655 // fall through and cancel
656 } catch (TimeoutException e) {
657 // fall through and cancel
658 } catch (CancellationException e) {
659 return false;
660 } catch (ExecutionException e) {
661 final Throwable cause = e.getCause();
662 if (cause instanceof IOException) {
663 return false;
664 } else if (cause instanceof UnsupportedOperationException) {
665 return false;
666 } else if (cause instanceof AuthenticatorException) {
667 return false;
668 } else if (cause instanceof RuntimeException) {
669 throw (RuntimeException) cause;
670 } else if (cause instanceof Error) {
671 throw (Error) cause;
672 } else {
673 throw new IllegalStateException(cause);
674 }
675 } finally {
676 cancel(true /* interrupt if running */);
677 }
678 return false;
679 }
680
681 public Boolean getResult() throws OperationCanceledException {
682 return internalGetResult(null, null);
683 }
684
685 public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
686 return internalGetResult(timeout, unit);
687 }
688
689 private class Response extends IAccountManagerResponse.Stub {
690 public void onResult(Bundle bundle) {
691 try {
692 if (bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) {
693 set(bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY));
694 return;
695 }
696 } catch (ClassCastException e) {
697 // we will set the exception below
698 }
699 onError(Constants.ERROR_CODE_INVALID_RESPONSE, "no result in response");
700 }
701
702 public void onError(int code, String message) {
703 if (code == Constants.ERROR_CODE_CANCELED) {
704 cancel(true /* mayInterruptIfRunning */);
705 return;
706 }
707 setException(convertErrorToException(code, message));
708 }
709 }
710
Fred Quintana60307342009-03-24 22:48:12 -0700711 }
712
Fred Quintanaa698f422009-04-08 19:14:54 -0700713 private Exception convertErrorToException(int code, String message) {
714 if (code == Constants.ERROR_CODE_NETWORK_ERROR) {
715 return new IOException(message);
Fred Quintana60307342009-03-24 22:48:12 -0700716 }
Fred Quintana60307342009-03-24 22:48:12 -0700717
Fred Quintanaa698f422009-04-08 19:14:54 -0700718 if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) {
719 return new UnsupportedOperationException();
Fred Quintana60307342009-03-24 22:48:12 -0700720 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700721
722 if (code == Constants.ERROR_CODE_INVALID_RESPONSE) {
723 return new AuthenticatorException("invalid response");
724 }
725
726 return new AuthenticatorException("unknown error code");
Fred Quintana60307342009-03-24 22:48:12 -0700727 }
728}