Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.accounts; |
| 18 | |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 19 | import android.app.Activity; |
| 20 | import android.content.Intent; |
| 21 | import android.content.Context; |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 22 | import android.os.Bundle; |
| 23 | import android.os.Handler; |
| 24 | import android.os.Looper; |
| 25 | import android.os.RemoteException; |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 26 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 27 | import java.io.IOException; |
| 28 | import java.util.concurrent.Callable; |
| 29 | import java.util.concurrent.CancellationException; |
| 30 | import java.util.concurrent.ExecutionException; |
| 31 | import java.util.concurrent.FutureTask; |
| 32 | import java.util.concurrent.TimeoutException; |
| 33 | import java.util.concurrent.TimeUnit; |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 34 | |
| 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 40 | * AccountManager accountManager = AccountManager.get(context); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 41 | * |
| 42 | * <p> |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 43 | * TODO(fredq) this interface is still in flux |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 44 | */ |
| 45 | public 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 56 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 62 | 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 70 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 81 | 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 89 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 117 | public Account[] blockingGetAccounts() { |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 118 | ensureNotOnMainThread(); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 119 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 127 | public Account[] blockingGetAccountsByType(String accountType) { |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 128 | ensureNotOnMainThread(); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 129 | 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 137 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 156 | 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 164 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 176 | try { |
| 177 | mService.removeAccount(account); |
| 178 | } catch (RemoteException e) { |
| 179 | // if this happens the entire runtime will restart |
| 180 | } |
| 181 | } |
| 182 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 183 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 195 | try { |
| 196 | mService.invalidateAuthToken(accountType, authToken); |
| 197 | } catch (RemoteException e) { |
| 198 | // if this happens the entire runtime will restart |
| 199 | } |
| 200 | } |
| 201 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 202 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 214 | 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 Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 222 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 233 | try { |
| 234 | mService.setPassword(account, password); |
| 235 | } catch (RemoteException e) { |
| 236 | // if this happens the entire runtime will restart |
| 237 | } |
| 238 | } |
| 239 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 240 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 252 | try { |
| 253 | mService.clearPassword(account); |
| 254 | } catch (RemoteException e) { |
| 255 | // if this happens the entire runtime will restart |
| 256 | } |
| 257 | } |
| 258 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 259 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 271 | try { |
| 272 | mService.setUserData(account, key, value); |
| 273 | } catch (RemoteException e) { |
| 274 | // if this happens the entire runtime will restart |
| 275 | } |
| 276 | } |
| 277 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 278 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 286 | } |
| 287 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 288 | public void blockingSetAuthToken(Account account, String authTokenType, String authToken) { |
| 289 | ensureNotOnMainThread(); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 290 | try { |
| 291 | mService.setAuthToken(account, authTokenType, authToken); |
| 292 | } catch (RemoteException e) { |
| 293 | // if this happens the entire runtime will restart |
| 294 | } |
| 295 | } |
| 296 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 297 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 420 | } |
| 421 | } |
| 422 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 423 | private void postToHandler(Handler handler, final Future2Callback callback, |
| 424 | final Future2 future) { |
| 425 | if (handler == null) { |
| 426 | handler = new Handler(mContext.getMainLooper()); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 427 | } |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 428 | final Handler innerHandler = handler; |
| 429 | innerHandler.post(new Runnable() { |
| 430 | public void run() { |
| 431 | callback.run(future); |
| 432 | } |
| 433 | }); |
| 434 | } |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 435 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 436 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 448 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 449 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 456 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 457 | private class FutureTaskWithCallback<V> extends FutureTask<V> implements Future1<V> { |
| 458 | final Future1Callback<V> mCallback; |
| 459 | final Handler mHandler; |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 460 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 461 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 471 | } |
| 472 | } |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 473 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 474 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 502 | } |
| 503 | } |
| 504 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 505 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 531 | } |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 532 | |
| 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 610 | } |
| 611 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 612 | 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 636 | } |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 637 | |
| 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 Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 711 | } |
| 712 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 713 | private Exception convertErrorToException(int code, String message) { |
| 714 | if (code == Constants.ERROR_CODE_NETWORK_ERROR) { |
| 715 | return new IOException(message); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 716 | } |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 717 | |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 718 | if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) { |
| 719 | return new UnsupportedOperationException(); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 720 | } |
Fred Quintana | a698f42 | 2009-04-08 19:14:54 -0700 | [diff] [blame^] | 721 | |
| 722 | if (code == Constants.ERROR_CODE_INVALID_RESPONSE) { |
| 723 | return new AuthenticatorException("invalid response"); |
| 724 | } |
| 725 | |
| 726 | return new AuthenticatorException("unknown error code"); |
Fred Quintana | 6030734 | 2009-03-24 22:48:12 -0700 | [diff] [blame] | 727 | } |
| 728 | } |