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.cts; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.accounts.AccountManagerCallback; 22 import android.accounts.AccountManagerFuture; 23 import android.accounts.AuthenticatorDescription; 24 import android.accounts.AuthenticatorException; 25 import android.accounts.OnAccountsUpdateListener; 26 import android.accounts.OperationCanceledException; 27 import android.app.Activity; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.HandlerThread; 33 import android.os.Looper; 34 import android.os.StrictMode; 35 import android.platform.test.annotations.Presubmit; 36 import android.test.ActivityInstrumentationTestCase2; 37 38 import java.io.IOException; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.concurrent.CountDownLatch; 42 import java.util.concurrent.TimeUnit; 43 import java.util.concurrent.atomic.AtomicReference; 44 45 /** 46 * You can run those unit tests with the following command line: 47 * 48 * adb shell am instrument 49 * -e debug false -w 50 * -e class android.accounts.cts.AccountManagerTest 51 * android.accounts.cts/android.support.test.runner.AndroidJUnitRunner 52 */ 53 public class AccountManagerTest extends ActivityInstrumentationTestCase2<AccountDummyActivity> { 54 55 public static final String ACCOUNT_NAME = "android.accounts.cts.account.name"; 56 public static final String ACCOUNT_NEW_NAME = "android.accounts.cts.account.name.rename"; 57 public static final String ACCOUNT_NAME_OTHER = "android.accounts.cts.account.name.other"; 58 59 public static final String ACCOUNT_TYPE = "android.accounts.cts.account.type"; 60 public static final String ACCOUNT_TYPE_CUSTOM = "android.accounts.cts.custom.account.type"; 61 public static final String ACCOUNT_TYPE_ABSENT = "android.accounts.cts.account.type.absent"; 62 63 public static final String ACCOUNT_PASSWORD = "android.accounts.cts.account.password"; 64 65 public static final String AUTH_TOKEN_TYPE = "mockAuthTokenType"; 66 public static final String AUTH_EXPIRING_TOKEN_TYPE = "mockAuthExpiringTokenType"; 67 public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel"; 68 public static final long AUTH_TOKEN_DURATION_MILLIS = 10000L; // Ten seconds. 69 70 public static final String FEATURE_1 = "feature.1"; 71 public static final String FEATURE_2 = "feature.2"; 72 public static final String NON_EXISTING_FEATURE = "feature.3"; 73 74 public static final String OPTION_NAME_1 = "option.name.1"; 75 public static final String OPTION_VALUE_1 = "option.value.1"; 76 77 public static final String OPTION_NAME_2 = "option.name.2"; 78 public static final String OPTION_VALUE_2 = "option.value.2"; 79 80 public static final String[] REQUIRED_FEATURES = new String[] { FEATURE_1, FEATURE_2 }; 81 82 public static final Bundle OPTIONS_BUNDLE = new Bundle(); 83 84 public static final Bundle USERDATA_BUNDLE = new Bundle(); 85 86 public static final String USERDATA_NAME_1 = "user.data.name.1"; 87 public static final String USERDATA_NAME_2 = "user.data.name.2"; 88 public static final String USERDATA_VALUE_1 = "user.data.value.1"; 89 public static final String USERDATA_VALUE_2 = "user.data.value.2"; 90 91 public static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE); 92 public static final Account ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API = new Account( 93 MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE); 94 public static final Account ACCOUNT_FOR_DEFAULT_IMPL = new Account( 95 MockAccountAuthenticator.ACCOUNT_NAME_FOR_DEFAULT_IMPL, ACCOUNT_TYPE); 96 public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE); 97 98 public static final Account CUSTOM_TOKEN_ACCOUNT = 99 new Account(ACCOUNT_NAME,ACCOUNT_TYPE_CUSTOM); 100 101 private static MockAccountAuthenticator mockAuthenticator; 102 private static final int LATCH_TIMEOUT_MS = 500; 103 private static AccountManager am; 104 getMockAuthenticator(Context context)105 public synchronized static MockAccountAuthenticator getMockAuthenticator(Context context) { 106 if (null == mockAuthenticator) { 107 mockAuthenticator = new MockAccountAuthenticator(context); 108 } 109 return mockAuthenticator; 110 } 111 112 private Activity mActivity; 113 private Context mContext; 114 AccountManagerTest()115 public AccountManagerTest() { 116 super(AccountDummyActivity.class); 117 } 118 119 @Override setUp()120 public void setUp() throws Exception { 121 super.setUp(); 122 mActivity = getActivity(); 123 mContext = getInstrumentation().getTargetContext(); 124 125 OPTIONS_BUNDLE.putString(OPTION_NAME_1, OPTION_VALUE_1); 126 OPTIONS_BUNDLE.putString(OPTION_NAME_2, OPTION_VALUE_2); 127 128 USERDATA_BUNDLE.putString(USERDATA_NAME_1, USERDATA_VALUE_1); 129 130 getMockAuthenticator(mContext); 131 132 am = AccountManager.get(mContext); 133 } 134 135 @Override tearDown()136 public void tearDown() throws Exception, AuthenticatorException, OperationCanceledException { 137 mockAuthenticator.clearData(); 138 139 // Need to clean up created account 140 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 141 AccountManager.KEY_BOOLEAN_RESULT)); 142 assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean( 143 AccountManager.KEY_BOOLEAN_RESULT)); 144 145 // Clean out any other accounts added during the tests. 146 Account[] ctsAccounts = am.getAccountsByType(ACCOUNT_TYPE); 147 Account[] ctsCustomAccounts = am.getAccountsByType(ACCOUNT_TYPE_CUSTOM); 148 ArrayList<Account> accounts = new ArrayList<>(Arrays.asList(ctsAccounts)); 149 accounts.addAll(Arrays.asList(ctsCustomAccounts)); 150 for (Account ctsAccount : accounts) { 151 removeAccount(am, ctsAccount, mActivity, null /* callback */); 152 } 153 154 // need to clean up the authenticator cached data 155 mockAuthenticator.clearData(); 156 157 super.tearDown(); 158 } 159 160 interface TokenFetcher { fetch(String tokenType)161 public Bundle fetch(String tokenType) 162 throws OperationCanceledException, AuthenticatorException, IOException; getAccount()163 public Account getAccount(); 164 } 165 validateSuccessfulTokenFetchingLifecycle(TokenFetcher fetcher, String tokenType)166 private void validateSuccessfulTokenFetchingLifecycle(TokenFetcher fetcher, String tokenType) 167 throws OperationCanceledException, AuthenticatorException, IOException { 168 Account account = fetcher.getAccount(); 169 Bundle expected = new Bundle(); 170 expected.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 171 expected.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 172 173 // First fetch. 174 Bundle actual = fetcher.fetch(tokenType); 175 assertTrue(mockAuthenticator.isRecentlyCalled()); 176 validateAccountAndAuthTokenResult(expected, actual); 177 178 /* 179 * On the second fetch the cache will be populated if we are using a authenticator with 180 * customTokens=false or we are using a scope that will cause the authenticator to set an 181 * expiration time (and that expiration time hasn't been reached). 182 */ 183 actual = fetcher.fetch(tokenType); 184 185 boolean isCachingExpected = 186 ACCOUNT_TYPE.equals(account.type) || AUTH_EXPIRING_TOKEN_TYPE.equals(tokenType); 187 assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled()); 188 validateAccountAndAuthTokenResult(expected, actual); 189 190 try { 191 // Delay further execution until expiring tokens can actually expire. 192 Thread.sleep(mockAuthenticator.getTokenDurationMillis() + 1L); 193 } catch (InterruptedException e) { 194 throw new RuntimeException(e); 195 } 196 197 /* 198 * With the time shift above, the third request will result in cache hits only from 199 * customToken=false authenticators. 200 */ 201 actual = fetcher.fetch(tokenType); 202 isCachingExpected = ACCOUNT_TYPE.equals(account.type); 203 assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled()); 204 validateAccountAndAuthTokenResult(expected, actual); 205 206 // invalidate token 207 String token = actual.getString(AccountManager.KEY_AUTHTOKEN); 208 am.invalidateAuthToken(account.type, token); 209 210 /* 211 * Upon invalidating the token, the cache should be clear regardless of authenticator. 212 */ 213 actual = fetcher.fetch(tokenType); 214 assertTrue(mockAuthenticator.isRecentlyCalled()); 215 validateAccountAndAuthTokenResult(expected, actual); 216 } 217 validateAccountAndAuthTokenResult(Bundle actual)218 private void validateAccountAndAuthTokenResult(Bundle actual) { 219 assertEquals( 220 ACCOUNT.name, 221 actual.get(AccountManager.KEY_ACCOUNT_NAME)); 222 assertEquals( 223 ACCOUNT.type, 224 actual.get(AccountManager.KEY_ACCOUNT_TYPE)); 225 assertEquals( 226 mockAuthenticator.getLastTokenServed(), 227 actual.get(AccountManager.KEY_AUTHTOKEN)); 228 } 229 validateAccountAndAuthTokenResult(Bundle expected, Bundle actual)230 private void validateAccountAndAuthTokenResult(Bundle expected, Bundle actual) { 231 assertEquals( 232 expected.get(AccountManager.KEY_ACCOUNT_NAME), 233 actual.get(AccountManager.KEY_ACCOUNT_NAME)); 234 assertEquals( 235 expected.get(AccountManager.KEY_ACCOUNT_TYPE), 236 actual.get(AccountManager.KEY_ACCOUNT_TYPE)); 237 assertEquals( 238 mockAuthenticator.getLastTokenServed(), 239 actual.get(AccountManager.KEY_AUTHTOKEN)); 240 } 241 validateAccountAndNoAuthTokenResult(Bundle result)242 private void validateAccountAndNoAuthTokenResult(Bundle result) { 243 assertEquals(ACCOUNT_NAME, result.get(AccountManager.KEY_ACCOUNT_NAME)); 244 assertEquals(ACCOUNT_TYPE, result.get(AccountManager.KEY_ACCOUNT_TYPE)); 245 assertNull(result.get(AccountManager.KEY_AUTHTOKEN)); 246 } 247 validateNullResult(Bundle resultBundle)248 private void validateNullResult(Bundle resultBundle) { 249 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_NAME)); 250 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_TYPE)); 251 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 252 } 253 validateAccountAndAuthTokenType()254 private void validateAccountAndAuthTokenType() { 255 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 256 assertEquals(AUTH_TOKEN_TYPE, mockAuthenticator.getAuthTokenType()); 257 } 258 validateFeatures()259 private void validateFeatures() { 260 assertEquals(REQUIRED_FEATURES[0], mockAuthenticator.getRequiredFeatures()[0]); 261 assertEquals(REQUIRED_FEATURES[1], mockAuthenticator.getRequiredFeatures()[1]); 262 } 263 validateOptions(Bundle expectedOptions, Bundle actualOptions)264 private void validateOptions(Bundle expectedOptions, Bundle actualOptions) { 265 // In ICS AccountManager may add options to indicate the caller id. 266 // We only validate that the passed in options are present in the actual ones 267 if (expectedOptions != null) { 268 assertNotNull(actualOptions); 269 assertEquals(expectedOptions.get(OPTION_NAME_1), actualOptions.get(OPTION_NAME_1)); 270 assertEquals(expectedOptions.get(OPTION_NAME_2), actualOptions.get(OPTION_NAME_2)); 271 } 272 } 273 validateSystemOptions(Bundle options)274 private void validateSystemOptions(Bundle options) { 275 assertNotNull(options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME)); 276 assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID)); 277 assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID)); 278 } 279 validateCredentials()280 private void validateCredentials() { 281 assertEquals(ACCOUNT, mockAuthenticator.getAccount()); 282 } 283 getAccountsCount()284 private int getAccountsCount() { 285 Account[] accounts = am.getAccounts(); 286 assertNotNull(accounts); 287 return accounts.length; 288 } 289 addAccount(AccountManager am, String accountType, String authTokenType, String[] requiredFeatures, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)290 private Bundle addAccount(AccountManager am, String accountType, String authTokenType, 291 String[] requiredFeatures, Bundle options, Activity activity, 292 AccountManagerCallback<Bundle> callback, Handler handler) throws 293 IOException, AuthenticatorException, OperationCanceledException { 294 295 AccountManagerFuture<Bundle> futureBundle = am.addAccount( 296 accountType, 297 authTokenType, 298 requiredFeatures, 299 options, 300 activity, 301 callback, 302 handler); 303 304 Bundle resultBundle = futureBundle.getResult(); 305 assertTrue(futureBundle.isDone()); 306 assertNotNull(resultBundle); 307 308 return resultBundle; 309 } 310 renameAccount(AccountManager am, Account account, String newName)311 private Account renameAccount(AccountManager am, Account account, String newName) 312 throws OperationCanceledException, AuthenticatorException, IOException { 313 AccountManagerFuture<Account> futureAccount = am.renameAccount( 314 account, newName, null /* callback */, null /* handler */); 315 Account renamedAccount = futureAccount.getResult(); 316 assertTrue(futureAccount.isDone()); 317 assertNotNull(renamedAccount); 318 return renamedAccount; 319 } 320 removeAccount(AccountManager am, Account account, AccountManagerCallback<Boolean> callback)321 private boolean removeAccount(AccountManager am, Account account, 322 AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException, 323 OperationCanceledException { 324 AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account, 325 callback, 326 null /* handler */); 327 Boolean resultBoolean = futureBoolean.getResult(); 328 assertTrue(futureBoolean.isDone()); 329 330 return resultBoolean; 331 } 332 removeAccountWithIntentLaunch(AccountManager am, Account account, Activity activity, AccountManagerCallback<Bundle> callback)333 private Bundle removeAccountWithIntentLaunch(AccountManager am, Account account, 334 Activity activity, AccountManagerCallback<Bundle> callback) throws IOException, 335 AuthenticatorException, OperationCanceledException { 336 337 AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account, 338 activity, 339 callback, 340 null /* handler */); 341 Bundle resultBundle = futureBundle.getResult(); 342 assertTrue(futureBundle.isDone()); 343 344 return resultBundle; 345 } 346 removeAccount(AccountManager am, Account account, Activity activity, AccountManagerCallback<Bundle> callback)347 private Bundle removeAccount(AccountManager am, Account account, Activity activity, 348 AccountManagerCallback<Bundle> callback) throws IOException, AuthenticatorException, 349 OperationCanceledException { 350 351 AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account, 352 activity, 353 callback, 354 null /* handler */); 355 Bundle resultBundle = futureBundle.getResult(); 356 assertTrue(futureBundle.isDone()); 357 358 return resultBundle; 359 } 360 removeAccountExplicitly(AccountManager am, Account account)361 private boolean removeAccountExplicitly(AccountManager am, Account account) { 362 return am.removeAccountExplicitly(account); 363 } 364 addAccountExplicitly(Account account, String password, Bundle userdata)365 private void addAccountExplicitly(Account account, String password, Bundle userdata) { 366 assertTrue(am.addAccountExplicitly(account, password, userdata)); 367 } 368 getAuthTokenByFeature(String[] features, Activity activity)369 private Bundle getAuthTokenByFeature(String[] features, Activity activity) 370 throws IOException, AuthenticatorException, OperationCanceledException { 371 372 AccountManagerFuture<Bundle> futureBundle = am.getAuthTokenByFeatures(ACCOUNT_TYPE, 373 AUTH_TOKEN_TYPE, 374 features, 375 activity, 376 OPTIONS_BUNDLE, 377 OPTIONS_BUNDLE, 378 null /* no callback */, 379 null /* no handler */ 380 ); 381 382 Bundle resultBundle = futureBundle.getResult(); 383 384 assertTrue(futureBundle.isDone()); 385 assertNotNull(resultBundle); 386 387 return resultBundle; 388 } 389 isAccountPresent(Account[] accounts, Account accountToCheck)390 private boolean isAccountPresent(Account[] accounts, Account accountToCheck) { 391 if (null == accounts || null == accountToCheck) { 392 return false; 393 } 394 boolean result = false; 395 int length = accounts.length; 396 for (int n=0; n<length; n++) { 397 if(accountToCheck.equals(accounts[n])) { 398 result = true; 399 break; 400 } 401 } 402 return result; 403 } 404 405 /** 406 * Test singleton 407 */ testGet()408 public void testGet() { 409 assertNotNull(AccountManager.get(mContext)); 410 } 411 412 /** 413 * Test creation of intent 414 */ testNewChooseAccountIntent()415 public void testNewChooseAccountIntent() { 416 Intent intent = AccountManager.newChooseAccountIntent(null, null, null, 417 null, null, 418 null, null); 419 assertNotNull(intent); 420 } 421 422 /** 423 * Test creation of intent 424 */ testNewChooseAccountIntentDepracated()425 public void testNewChooseAccountIntentDepracated() { 426 Intent intent = AccountManager.newChooseAccountIntent(null, null, null, false, 427 null, null, 428 null, null); 429 assertNotNull(intent); 430 } 431 432 /** 433 * Test a basic addAccount() 434 */ testAddAccount()435 public void testAddAccount() throws IOException, AuthenticatorException, 436 OperationCanceledException { 437 438 Bundle resultBundle = addAccount(am, 439 ACCOUNT_TYPE, 440 AUTH_TOKEN_TYPE, 441 REQUIRED_FEATURES, 442 OPTIONS_BUNDLE, 443 mActivity, 444 null /* callback */, 445 null /* handler */); 446 447 // Assert parameters has been passed correctly 448 validateAccountAndAuthTokenType(); 449 validateFeatures(); 450 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 451 validateSystemOptions(mockAuthenticator.mOptionsAddAccount); 452 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 453 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 454 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 455 456 // Assert returned result 457 validateAccountAndNoAuthTokenResult(resultBundle); 458 } 459 460 /** 461 * Test addAccount() with callback and handler 462 */ testAddAccountWithCallbackAndHandler()463 public void testAddAccountWithCallbackAndHandler() throws IOException, 464 AuthenticatorException, OperationCanceledException { 465 466 testAddAccountWithCallbackAndHandler(null /* handler */); 467 testAddAccountWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 468 } 469 470 /** 471 * Test addAccount() with no associated account authenticator 472 */ testAddAccountWithNoAuthenticator()473 public void testAddAccountWithNoAuthenticator() throws IOException, 474 AuthenticatorException, OperationCanceledException { 475 476 try { 477 AccountManagerFuture<Bundle> futureBundle = am.addAccount( 478 "nonExistingAccountType", 479 null, 480 null, 481 null, 482 null, 483 null, 484 null); 485 486 futureBundle.getResult(); 487 fail(); 488 } catch (AuthenticatorException expectedException) { 489 return; 490 } 491 } 492 testAddAccountWithCallbackAndHandler(Handler handler)493 private void testAddAccountWithCallbackAndHandler(Handler handler) throws IOException, 494 AuthenticatorException, OperationCanceledException { 495 496 final CountDownLatch latch = new CountDownLatch(1); 497 498 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 499 @Override 500 public void run(AccountManagerFuture<Bundle> bundleFuture) { 501 Bundle resultBundle = null; 502 try { 503 resultBundle = bundleFuture.getResult(); 504 } catch (OperationCanceledException e) { 505 fail("should not throw an OperationCanceledException"); 506 } catch (IOException e) { 507 fail("should not throw an IOException"); 508 } catch (AuthenticatorException e) { 509 fail("should not throw an AuthenticatorException"); 510 } 511 512 // Assert parameters has been passed correctly 513 validateAccountAndAuthTokenType(); 514 validateFeatures(); 515 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 516 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 517 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 518 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 519 520 // Assert return result 521 validateAccountAndNoAuthTokenResult(resultBundle); 522 523 latch.countDown(); 524 } 525 }; 526 527 addAccount(am, 528 ACCOUNT_TYPE, 529 AUTH_TOKEN_TYPE, 530 REQUIRED_FEATURES, 531 OPTIONS_BUNDLE, 532 mActivity, 533 callback, 534 handler); 535 536 // Wait with timeout for the callback to do its work 537 try { 538 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 539 } catch (InterruptedException e) { 540 fail("should not throw an InterruptedException"); 541 } 542 } 543 544 /** 545 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 546 */ testAddAccountExplicitlyAndRemoveAccount()547 public void testAddAccountExplicitlyAndRemoveAccount() throws IOException, 548 AuthenticatorException, OperationCanceledException { 549 550 final int expectedAccountsCount = getAccountsCount(); 551 552 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 553 554 // Assert that we have one more account 555 Account[] accounts = am.getAccounts(); 556 assertNotNull(accounts); 557 assertEquals(1 + expectedAccountsCount, accounts.length); 558 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 559 // Need to clean up 560 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 561 AccountManager.KEY_BOOLEAN_RESULT)); 562 563 // and verify that we go back to the initial state 564 accounts = am.getAccounts(); 565 assertNotNull(accounts); 566 assertEquals(expectedAccountsCount, accounts.length); 567 } 568 569 /** 570 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 571 */ testAddAccountExplicitlyAndRemoveAccountWithNewApi()572 public void testAddAccountExplicitlyAndRemoveAccountWithNewApi() throws IOException, 573 AuthenticatorException, OperationCanceledException { 574 575 final int expectedAccountsCount = getAccountsCount(); 576 577 addAccountExplicitly(ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, ACCOUNT_PASSWORD, null /* userData */); 578 579 // Assert that we have one more account 580 Account[] accounts = am.getAccounts(); 581 assertNotNull(accounts); 582 assertEquals(1 + expectedAccountsCount, accounts.length); 583 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API)); 584 // Deprecated API should not work 585 assertFalse(removeAccount(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, null /* callback */)); 586 accounts = am.getAccounts(); 587 assertNotNull(accounts); 588 assertEquals(1 + expectedAccountsCount, accounts.length); 589 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API)); 590 // Check removal of account 591 assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, mActivity, null /* callback */) 592 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)); 593 // and verify that we go back to the initial state 594 accounts = am.getAccounts(); 595 assertNotNull(accounts); 596 assertEquals(expectedAccountsCount, accounts.length); 597 } 598 599 /** 600 * Test addAccountExplicitly(), renameAccount() and removeAccount() calling 601 * into default implementations. 602 */ testAddAccountExplicitlyAndRemoveAccountWithDefaultImpl()603 public void testAddAccountExplicitlyAndRemoveAccountWithDefaultImpl() throws IOException, 604 AuthenticatorException, OperationCanceledException { 605 606 final int expectedAccountsCount = getAccountsCount(); 607 608 addAccountExplicitly(ACCOUNT_FOR_DEFAULT_IMPL, ACCOUNT_PASSWORD, null /* userData */); 609 610 // Assert that we have one more account 611 Account[] accounts = am.getAccounts(); 612 assertNotNull(accounts); 613 assertEquals(1 + expectedAccountsCount, accounts.length); 614 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_DEFAULT_IMPL)); 615 // Check removal of account 616 assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_DEFAULT_IMPL, mActivity, null /* callback */) 617 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)); 618 // and verify that we go back to the initial state 619 accounts = am.getAccounts(); 620 assertNotNull(accounts); 621 assertEquals(expectedAccountsCount, accounts.length); 622 } 623 624 /** 625 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 626 */ testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi()627 public void testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi() throws IOException, 628 AuthenticatorException, OperationCanceledException { 629 630 final int expectedAccountsCount = getAccountsCount(); 631 632 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 633 634 // Assert that we have one more account 635 Account[] accounts = am.getAccounts(); 636 assertNotNull(accounts); 637 assertEquals(1 + expectedAccountsCount, accounts.length); 638 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 639 // Need to clean up 640 assertTrue(removeAccount(am, ACCOUNT, null /* callback */)); 641 642 // and verify that we go back to the initial state 643 accounts = am.getAccounts(); 644 assertNotNull(accounts); 645 assertEquals(expectedAccountsCount, accounts.length); 646 } 647 648 /** 649 * Test addAccountExplicitly() and removeAccountExplictly(). 650 */ testAddAccountExplicitlyAndRemoveAccountExplicitly()651 public void testAddAccountExplicitlyAndRemoveAccountExplicitly() { 652 final int expectedAccountsCount = getAccountsCount(); 653 654 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 655 656 // Assert that we have one more account 657 Account[] accounts = am.getAccounts(); 658 assertNotNull(accounts); 659 assertEquals(1 + expectedAccountsCount, accounts.length); 660 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 661 // Need to clean up 662 assertTrue(removeAccountExplicitly(am, ACCOUNT)); 663 664 // and verify that we go back to the initial state 665 accounts = am.getAccounts(); 666 assertNotNull(accounts); 667 assertEquals(expectedAccountsCount, accounts.length); 668 } 669 670 /** 671 * Test setUserData() and getUserData(). 672 */ testAccountRenameAndGetPreviousName()673 public void testAccountRenameAndGetPreviousName() 674 throws OperationCanceledException, AuthenticatorException, IOException { 675 // Add a first account 676 boolean result = am.addAccountExplicitly(ACCOUNT, 677 ACCOUNT_PASSWORD, 678 USERDATA_BUNDLE); 679 assertTrue(result); 680 681 // Prior to a renmae, the previous name should be null. 682 String nullName = am.getPreviousName(ACCOUNT); 683 assertNull(nullName); 684 685 final int expectedAccountsCount = getAccountsCount(); 686 687 Account renamedAccount = renameAccount(am, ACCOUNT, ACCOUNT_NEW_NAME); 688 689 /* 690 * Make sure that the resultant renamed account has the correct name 691 * and is associated with the correct account type. 692 */ 693 assertEquals(ACCOUNT_NEW_NAME, renamedAccount.name); 694 assertEquals(ACCOUNT.type, renamedAccount.type); 695 696 // Make sure the total number of accounts is the same. 697 Account[] accounts = am.getAccounts(); 698 assertEquals(expectedAccountsCount, accounts.length); 699 700 // Make sure the old account isn't present. 701 assertFalse(isAccountPresent(am.getAccounts(), ACCOUNT)); 702 703 // But that the new one is. 704 assertTrue(isAccountPresent(am.getAccounts(), renamedAccount)); 705 706 // Check that the UserData is still present. 707 assertEquals(USERDATA_VALUE_1, am.getUserData(renamedAccount, USERDATA_NAME_1)); 708 709 assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount)); 710 711 // Need to clean up 712 assertTrue(removeAccount(am, renamedAccount, mActivity, null /* callback */).getBoolean( 713 AccountManager.KEY_BOOLEAN_RESULT)); 714 } 715 716 /** 717 * Test getAccounts() and getAccountsByType() 718 */ testGetAccountsAndGetAccountsByType()719 public void testGetAccountsAndGetAccountsByType() { 720 721 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT)); 722 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT_SAME_TYPE)); 723 724 final int accountsCount = getAccountsCount(); 725 726 // Add a first account 727 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 728 729 // Check that we have the new account 730 Account[] accounts = am.getAccounts(); 731 assertEquals(1 + accountsCount, accounts.length); 732 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 733 734 // Add another account 735 addAccountExplicitly(ACCOUNT_SAME_TYPE, ACCOUNT_PASSWORD, null /* userData */); 736 737 // Check that we have one more account again 738 accounts = am.getAccounts(); 739 assertEquals(2 + accountsCount, accounts.length); 740 assertEquals(true, isAccountPresent(accounts, ACCOUNT_SAME_TYPE)); 741 742 // Check if we have one from first type 743 accounts = am.getAccountsByType(ACCOUNT_TYPE); 744 assertEquals(2, accounts.length); 745 746 // Check if we dont have any account from the other type 747 accounts = am.getAccountsByType(ACCOUNT_TYPE_ABSENT); 748 assertEquals(0, accounts.length); 749 } 750 751 /** 752 * Test getAuthenticatorTypes() 753 */ testGetAuthenticatorTypes()754 public void testGetAuthenticatorTypes() { 755 AuthenticatorDescription[] types = am.getAuthenticatorTypes(); 756 for(AuthenticatorDescription description: types) { 757 if (description.type.equals(ACCOUNT_TYPE)) { 758 return; 759 } 760 } 761 fail("should have found Authenticator type: " + ACCOUNT_TYPE); 762 } 763 764 /** 765 * Test setPassword() and getPassword() 766 */ testSetAndGetAndClearPassword()767 public void testSetAndGetAndClearPassword() { 768 // Add a first account 769 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 770 771 // Check that the password is the one we defined 772 assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT)); 773 774 // Clear the password and check that it is cleared 775 am.clearPassword(ACCOUNT); 776 assertNull(am.getPassword(ACCOUNT)); 777 778 // Reset the password 779 am.setPassword(ACCOUNT, ACCOUNT_PASSWORD); 780 781 // Check that the password is the one we defined 782 assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT)); 783 } 784 785 /** 786 * Test setUserData() and getUserData() 787 */ testSetAndGetUserData()788 public void testSetAndGetUserData() { 789 // Add a first account 790 boolean result = am.addAccountExplicitly(ACCOUNT, 791 ACCOUNT_PASSWORD, 792 USERDATA_BUNDLE); 793 794 assertTrue(result); 795 796 // Check that the UserData is the one we defined 797 assertEquals(USERDATA_VALUE_1, am.getUserData(ACCOUNT, USERDATA_NAME_1)); 798 799 am.setUserData(ACCOUNT, USERDATA_NAME_2, USERDATA_VALUE_2); 800 801 // Check that the UserData is the one we defined 802 assertEquals(USERDATA_VALUE_2, am.getUserData(ACCOUNT, USERDATA_NAME_2)); 803 } 804 805 /** 806 * Test getAccountsByTypeAndFeatures() 807 */ testGetAccountsByTypeAndFeatures()808 public void testGetAccountsByTypeAndFeatures() throws IOException, 809 AuthenticatorException, OperationCanceledException { 810 811 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 812 813 AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures( 814 ACCOUNT_TYPE, REQUIRED_FEATURES, null, null); 815 816 Account[] accounts = futureAccounts.getResult(); 817 818 assertNotNull(accounts); 819 assertEquals(1, accounts.length); 820 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 821 822 futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, 823 new String[] { NON_EXISTING_FEATURE }, 824 null /* callback*/, 825 null /* handler */); 826 accounts = futureAccounts.getResult(); 827 828 assertNotNull(accounts); 829 assertEquals(0, accounts.length); 830 } 831 832 /** 833 * Test getAccountsByTypeAndFeatures() with callback and handler 834 */ testGetAccountsByTypeAndFeaturesWithCallbackAndHandler()835 public void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler() throws IOException, 836 AuthenticatorException, OperationCanceledException { 837 838 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 839 840 testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(null /* handler */); 841 testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 842 } 843 testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(Handler handler)844 private void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(Handler handler) throws 845 IOException, AuthenticatorException, OperationCanceledException { 846 847 final CountDownLatch latch1 = new CountDownLatch(1); 848 849 AccountManagerCallback<Account[]> callback1 = new AccountManagerCallback<Account[]>() { 850 @Override 851 public void run(AccountManagerFuture<Account[]> accountsFuture) { 852 try { 853 Account[] accounts = accountsFuture.getResult(); 854 assertNotNull(accounts); 855 assertEquals(1, accounts.length); 856 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 857 } catch (OperationCanceledException e) { 858 fail("should not throw an OperationCanceledException"); 859 } catch (IOException e) { 860 fail("should not throw an IOException"); 861 } catch (AuthenticatorException e) { 862 fail("should not throw an AuthenticatorException"); 863 } finally { 864 latch1.countDown(); 865 } 866 } 867 }; 868 869 AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures( 870 ACCOUNT_TYPE, 871 REQUIRED_FEATURES, 872 callback1, 873 handler); 874 875 Account[] accounts = futureAccounts.getResult(); 876 877 assertNotNull(accounts); 878 assertEquals(1, accounts.length); 879 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 880 881 // Wait with timeout for the callback to do its work 882 try { 883 latch1.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 884 } catch (InterruptedException e) { 885 fail("should not throw an InterruptedException"); 886 } 887 888 final CountDownLatch latch2 = new CountDownLatch(1); 889 890 AccountManagerCallback<Account[]> callback2 = new AccountManagerCallback<Account[]>() { 891 @Override 892 public void run(AccountManagerFuture<Account[]> accountsFuture) { 893 try { 894 Account[] accounts = accountsFuture.getResult(); 895 assertNotNull(accounts); 896 assertEquals(0, accounts.length); 897 } catch (OperationCanceledException e) { 898 fail("should not throw an OperationCanceledException"); 899 } catch (IOException e) { 900 fail("should not throw an IOException"); 901 } catch (AuthenticatorException e) { 902 fail("should not throw an AuthenticatorException"); 903 } finally { 904 latch2.countDown(); 905 } 906 } 907 }; 908 909 accounts = null; 910 911 futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, 912 new String[] { NON_EXISTING_FEATURE }, 913 callback2, 914 handler); 915 916 accounts = futureAccounts.getResult(); 917 assertNotNull(accounts); 918 assertEquals(0, accounts.length); 919 920 // Wait with timeout for the callback to do its work 921 try { 922 latch2.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 923 } catch (InterruptedException e) { 924 fail("should not throw an InterruptedException"); 925 } 926 } 927 928 /** 929 * Test setAuthToken() and peekAuthToken() 930 */ testSetAndPeekAndInvalidateAuthToken()931 public void testSetAndPeekAndInvalidateAuthToken() { 932 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 933 String expected = "x"; 934 am.setAuthToken(ACCOUNT, AUTH_TOKEN_TYPE, expected); 935 936 // Ask for the AuthToken 937 String token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE); 938 assertNotNull(token); 939 assertEquals(expected, token); 940 941 am.invalidateAuthToken(ACCOUNT_TYPE, token); 942 token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE); 943 assertNull(token); 944 } 945 946 /** 947 * Test successful blockingGetAuthToken() with customTokens=false authenticator. 948 */ testBlockingGetAuthToken_DefaultToken_Success()949 public void testBlockingGetAuthToken_DefaultToken_Success() 950 throws IOException, AuthenticatorException, OperationCanceledException { 951 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null); 952 953 String token = am.blockingGetAuthToken(ACCOUNT, 954 AUTH_TOKEN_TYPE, 955 false /* no failure notification */); 956 957 // Ask for the AuthToken 958 assertNotNull(token); 959 assertEquals(mockAuthenticator.getLastTokenServed(), token); 960 } 961 962 private static class BlockingGetAuthTokenFetcher implements TokenFetcher { 963 private final Account mAccount; 964 BlockingGetAuthTokenFetcher(Account account)965 BlockingGetAuthTokenFetcher(Account account) { 966 mAccount = account; 967 } 968 969 @Override fetch(String tokenType)970 public Bundle fetch(String tokenType) 971 throws OperationCanceledException, AuthenticatorException, IOException { 972 String token = am.blockingGetAuthToken( 973 getAccount(), 974 tokenType, 975 false /* no failure notification */); 976 Bundle result = new Bundle(); 977 result.putString(AccountManager.KEY_AUTHTOKEN, token); 978 result.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); 979 result.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type); 980 return result; 981 } 982 @Override getAccount()983 public Account getAccount() { 984 return CUSTOM_TOKEN_ACCOUNT; 985 } 986 } 987 988 /** 989 * Test successful blockingGetAuthToken() with customTokens=true authenticator. 990 */ testBlockingGetAuthToken_CustomToken_NoCaching_Success()991 public void testBlockingGetAuthToken_CustomToken_NoCaching_Success() 992 throws IOException, AuthenticatorException, OperationCanceledException { 993 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 994 TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT); 995 validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE); 996 } 997 998 /** 999 * Test successful blockingGetAuthToken() with customTokens=true authenticator. 1000 */ testBlockingGetAuthToken_CustomToken_ExpiringCache_Success()1001 public void testBlockingGetAuthToken_CustomToken_ExpiringCache_Success() 1002 throws IOException, AuthenticatorException, OperationCanceledException { 1003 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1004 TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT); 1005 validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE); 1006 } 1007 1008 /** 1009 * Test successful getAuthToken() using a future with customTokens=false authenticator. 1010 */ testDeprecatedGetAuthTokenWithFuture_NoOptions_DefaultToken_Success()1011 public void testDeprecatedGetAuthTokenWithFuture_NoOptions_DefaultToken_Success() 1012 throws IOException, AuthenticatorException, OperationCanceledException { 1013 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1014 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1015 AUTH_TOKEN_TYPE, 1016 false /* no failure notification */, 1017 null /* no callback */, 1018 null /* no handler */ 1019 ); 1020 1021 Bundle resultBundle = futureBundle.getResult(); 1022 1023 assertTrue(futureBundle.isDone()); 1024 assertNotNull(resultBundle); 1025 1026 // Assert returned result 1027 validateAccountAndAuthTokenResult(resultBundle); 1028 } 1029 1030 /** 1031 * Test successful getAuthToken() using a future with customTokens=false without 1032 * expiring tokens. 1033 */ testDeprecatedGetAuthTokenWithFuture_NoOptions_CustomToken_Success()1034 public void testDeprecatedGetAuthTokenWithFuture_NoOptions_CustomToken_Success() 1035 throws IOException, AuthenticatorException, OperationCanceledException { 1036 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1037 // validateSuccessfulTokenFetchingLifecycle(AccountManager am, TokenFetcher fetcher, String tokenType) 1038 TokenFetcher f = new TokenFetcher() { 1039 @Override 1040 public Bundle fetch(String tokenType) 1041 throws OperationCanceledException, AuthenticatorException, IOException { 1042 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken( 1043 getAccount(), 1044 tokenType, 1045 false /* no failure notification */, 1046 null /* no callback */, 1047 null /* no handler */ 1048 ); 1049 Bundle actual = futureBundle.getResult(); 1050 assertTrue(futureBundle.isDone()); 1051 assertNotNull(actual); 1052 return actual; 1053 } 1054 1055 @Override 1056 public Account getAccount() { 1057 return CUSTOM_TOKEN_ACCOUNT; 1058 } 1059 }; 1060 validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE); 1061 validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE); 1062 } 1063 1064 /** 1065 * Test successful getAuthToken() using a future with customTokens=false without 1066 * expiring tokens. 1067 */ testGetAuthTokenWithFuture_Options_DefaultToken_Success()1068 public void testGetAuthTokenWithFuture_Options_DefaultToken_Success() 1069 throws IOException, AuthenticatorException, OperationCanceledException { 1070 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1071 1072 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1073 AUTH_TOKEN_TYPE, 1074 OPTIONS_BUNDLE, 1075 mActivity, 1076 null /* no callback */, 1077 null /* no handler */ 1078 ); 1079 1080 Bundle resultBundle = futureBundle.getResult(); 1081 1082 assertTrue(futureBundle.isDone()); 1083 assertNotNull(resultBundle); 1084 1085 // Assert returned result 1086 validateAccountAndAuthTokenResult(resultBundle); 1087 1088 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1089 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1090 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1091 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsGetAuthToken); 1092 validateSystemOptions(mockAuthenticator.mOptionsGetAuthToken); 1093 } 1094 1095 /** 1096 * Test successful getAuthToken() using a future with customTokens=false without 1097 * expiring tokens. 1098 */ testGetAuthTokenWithFuture_Options_CustomToken_Success()1099 public void testGetAuthTokenWithFuture_Options_CustomToken_Success() 1100 throws IOException, AuthenticatorException, OperationCanceledException { 1101 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1102 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1103 @Override 1104 public Bundle fetch(String tokenType) 1105 throws OperationCanceledException, AuthenticatorException, IOException { 1106 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken( 1107 getAccount(), 1108 tokenType, 1109 OPTIONS_BUNDLE, 1110 false /* no failure notification */, 1111 null /* no callback */, 1112 null /* no handler */ 1113 ); 1114 Bundle actual = futureBundle.getResult(); 1115 assertTrue(futureBundle.isDone()); 1116 assertNotNull(actual); 1117 return actual; 1118 } 1119 1120 @Override 1121 public Account getAccount() { 1122 return CUSTOM_TOKEN_ACCOUNT; 1123 } 1124 }; 1125 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1126 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1127 } 1128 1129 1130 /** 1131 * Test successful getAuthToken() using a future with customTokens=false without 1132 * expiring tokens. 1133 */ testGetAuthTokenWithCallback_Options_Handler_DefaultToken_Success()1134 public void testGetAuthTokenWithCallback_Options_Handler_DefaultToken_Success() 1135 throws IOException, AuthenticatorException, OperationCanceledException { 1136 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null); 1137 final HandlerThread handlerThread = new HandlerThread("accounts.test"); 1138 handlerThread.start(); 1139 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1140 @Override 1141 public Bundle fetch(String tokenType) 1142 throws OperationCanceledException, AuthenticatorException, IOException { 1143 final AtomicReference<Bundle> actualRef = new AtomicReference<>(); 1144 final CountDownLatch latch = new CountDownLatch(1); 1145 1146 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1147 @Override 1148 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1149 Bundle resultBundle = null; 1150 try { 1151 resultBundle = bundleFuture.getResult(); 1152 actualRef.set(resultBundle); 1153 } catch (OperationCanceledException e) { 1154 fail("should not throw an OperationCanceledException"); 1155 } catch (IOException e) { 1156 fail("should not throw an IOException"); 1157 } catch (AuthenticatorException e) { 1158 fail("should not throw an AuthenticatorException"); 1159 } finally { 1160 latch.countDown(); 1161 } 1162 } 1163 }; 1164 1165 am.getAuthToken(getAccount(), 1166 tokenType, 1167 OPTIONS_BUNDLE, 1168 false /* no failure notification */, 1169 callback, 1170 new Handler(handlerThread.getLooper())); 1171 1172 // Wait with timeout for the callback to do its work 1173 try { 1174 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1175 } catch (InterruptedException e) { 1176 fail("should not throw an InterruptedException"); 1177 } 1178 return actualRef.get(); 1179 } 1180 1181 @Override 1182 public Account getAccount() { 1183 return ACCOUNT; 1184 } 1185 }; 1186 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1187 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1188 } 1189 1190 /** 1191 * Test successful getAuthToken() using a future with customTokens=false without 1192 * expiring tokens. 1193 */ testGetAuthTokenWithCallback_Options_Handler_CustomToken_Success()1194 public void testGetAuthTokenWithCallback_Options_Handler_CustomToken_Success() 1195 throws IOException, AuthenticatorException, OperationCanceledException { 1196 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1197 final HandlerThread handlerThread = new HandlerThread("accounts.test"); 1198 handlerThread.start(); 1199 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1200 @Override 1201 public Bundle fetch(String tokenType) 1202 throws OperationCanceledException, AuthenticatorException, IOException { 1203 final AtomicReference<Bundle> actualRef = new AtomicReference<>(); 1204 final CountDownLatch latch = new CountDownLatch(1); 1205 1206 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1207 @Override 1208 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1209 Bundle resultBundle = null; 1210 try { 1211 resultBundle = bundleFuture.getResult(); 1212 actualRef.set(resultBundle); 1213 } catch (OperationCanceledException e) { 1214 fail("should not throw an OperationCanceledException"); 1215 } catch (IOException e) { 1216 fail("should not throw an IOException"); 1217 } catch (AuthenticatorException e) { 1218 fail("should not throw an AuthenticatorException"); 1219 } finally { 1220 latch.countDown(); 1221 } 1222 } 1223 }; 1224 1225 am.getAuthToken(getAccount(), 1226 tokenType, 1227 OPTIONS_BUNDLE, 1228 false /* no failure notification */, 1229 callback, 1230 new Handler(handlerThread.getLooper())); 1231 1232 // Wait with timeout for the callback to do its work 1233 try { 1234 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1235 } catch (InterruptedException e) { 1236 fail("should not throw an InterruptedException"); 1237 } 1238 return actualRef.get(); 1239 } 1240 1241 @Override 1242 public Account getAccount() { 1243 return CUSTOM_TOKEN_ACCOUNT; 1244 } 1245 }; 1246 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1247 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1248 } 1249 1250 /** 1251 * Test getAuthToken() with callback and handler 1252 */ testGetAuthTokenWithCallbackAndHandler()1253 public void testGetAuthTokenWithCallbackAndHandler() throws IOException, AuthenticatorException, 1254 OperationCanceledException { 1255 1256 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1257 1258 testGetAuthTokenWithCallbackAndHandler(null /* handler */); 1259 testGetAuthTokenWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1260 } 1261 testGetAuthTokenWithCallbackAndHandler(Handler handler)1262 private void testGetAuthTokenWithCallbackAndHandler(Handler handler) throws IOException, 1263 AuthenticatorException, OperationCanceledException { 1264 1265 final CountDownLatch latch = new CountDownLatch(1); 1266 1267 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1268 @Override 1269 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1270 1271 Bundle resultBundle = null; 1272 try { 1273 resultBundle = bundleFuture.getResult(); 1274 1275 // Assert returned result 1276 validateAccountAndAuthTokenResult(resultBundle); 1277 1278 } catch (OperationCanceledException e) { 1279 fail("should not throw an OperationCanceledException"); 1280 } catch (IOException e) { 1281 fail("should not throw an IOException"); 1282 } catch (AuthenticatorException e) { 1283 fail("should not throw an AuthenticatorException"); 1284 } 1285 finally { 1286 latch.countDown(); 1287 } 1288 } 1289 }; 1290 1291 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1292 AUTH_TOKEN_TYPE, 1293 false /* no failure notification */, 1294 callback, 1295 handler 1296 ); 1297 1298 Bundle resultBundle = futureBundle.getResult(); 1299 1300 assertTrue(futureBundle.isDone()); 1301 assertNotNull(resultBundle); 1302 1303 // Wait with timeout for the callback to do its work 1304 try { 1305 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1306 } catch (InterruptedException e) { 1307 fail("should not throw an InterruptedException"); 1308 } 1309 } 1310 1311 /** 1312 * test getAuthToken() with options and callback and handler 1313 */ testGetAuthTokenWithOptionsAndCallback()1314 public void testGetAuthTokenWithOptionsAndCallback() throws IOException, 1315 AuthenticatorException, OperationCanceledException { 1316 1317 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1318 1319 testGetAuthTokenWithOptionsAndCallbackAndHandler(null /* handler */); 1320 testGetAuthTokenWithOptionsAndCallbackAndHandler(new Handler(Looper.getMainLooper())); 1321 } 1322 testGetAuthTokenWithOptionsAndCallbackAndHandler(Handler handler)1323 private void testGetAuthTokenWithOptionsAndCallbackAndHandler(Handler handler) throws 1324 IOException, AuthenticatorException, OperationCanceledException { 1325 1326 final CountDownLatch latch = new CountDownLatch(1); 1327 1328 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1329 @Override 1330 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1331 1332 Bundle resultBundle = null; 1333 try { 1334 resultBundle = bundleFuture.getResult(); 1335 // Assert returned result 1336 validateAccountAndAuthTokenResult(resultBundle); 1337 } catch (OperationCanceledException e) { 1338 fail("should not throw an OperationCanceledException"); 1339 } catch (IOException e) { 1340 fail("should not throw an IOException"); 1341 } catch (AuthenticatorException e) { 1342 fail("should not throw an AuthenticatorException"); 1343 } 1344 finally { 1345 latch.countDown(); 1346 } 1347 } 1348 }; 1349 1350 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1351 AUTH_TOKEN_TYPE, 1352 OPTIONS_BUNDLE, 1353 mActivity, 1354 callback, 1355 handler 1356 ); 1357 1358 Bundle resultBundle = futureBundle.getResult(); 1359 1360 assertTrue(futureBundle.isDone()); 1361 assertNotNull(resultBundle); 1362 1363 // Wait with timeout for the callback to do its work 1364 try { 1365 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1366 } catch (InterruptedException e) { 1367 fail("should not throw an InterruptedException"); 1368 } 1369 } 1370 1371 /** 1372 * Test getAuthTokenByFeatures() 1373 */ testGetAuthTokenByFeatures()1374 public void testGetAuthTokenByFeatures() throws IOException, AuthenticatorException, 1375 OperationCanceledException { 1376 1377 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1378 1379 Bundle resultBundle = getAuthTokenByFeature( 1380 new String[] { NON_EXISTING_FEATURE }, 1381 null /* activity */ 1382 ); 1383 1384 // Assert returned result 1385 validateNullResult(resultBundle); 1386 1387 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1388 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1389 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1390 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1391 1392 mockAuthenticator.clearData(); 1393 1394 // Now test with existing features and an activity 1395 resultBundle = getAuthTokenByFeature( 1396 new String[] { NON_EXISTING_FEATURE }, 1397 mActivity 1398 ); 1399 1400 // Assert returned result 1401 validateAccountAndAuthTokenResult(resultBundle); 1402 1403 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 1404 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1405 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1406 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1407 1408 mockAuthenticator.clearData(); 1409 1410 // Now test with existing features and no activity 1411 resultBundle = getAuthTokenByFeature( 1412 REQUIRED_FEATURES, 1413 null /* activity */ 1414 ); 1415 1416 // Assert returned result 1417 validateAccountAndAuthTokenResult(resultBundle); 1418 1419 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1420 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1421 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1422 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1423 1424 mockAuthenticator.clearData(); 1425 1426 // Now test with existing features and an activity 1427 resultBundle = getAuthTokenByFeature( 1428 REQUIRED_FEATURES, 1429 mActivity 1430 ); 1431 1432 // Assert returned result 1433 validateAccountAndAuthTokenResult(resultBundle); 1434 1435 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1436 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1437 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1438 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1439 } 1440 1441 /** 1442 * Test confirmCredentials() 1443 */ 1444 @Presubmit testConfirmCredentials()1445 public void testConfirmCredentials() throws IOException, AuthenticatorException, 1446 OperationCanceledException { 1447 1448 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1449 1450 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT, 1451 OPTIONS_BUNDLE, 1452 mActivity, 1453 null /* callback*/, 1454 null /* handler */); 1455 1456 futureBundle.getResult(); 1457 1458 // Assert returned result 1459 validateCredentials(); 1460 } 1461 1462 /** 1463 * Tests the setting of lastAuthenticatedTime on adding account 1464 */ testLastAuthenticatedTimeAfterAddAccount()1465 public void testLastAuthenticatedTimeAfterAddAccount() throws IOException, 1466 AuthenticatorException, OperationCanceledException { 1467 assertTrue(addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD) > 0); 1468 } 1469 1470 /** 1471 * Test confirmCredentials() for account not on device. Just that no error 1472 * should be thrown. 1473 */ testConfirmCredentialsAccountNotOnDevice()1474 public void testConfirmCredentialsAccountNotOnDevice() throws IOException, 1475 AuthenticatorException, OperationCanceledException { 1476 1477 Account account = new Account("AccountNotOnThisDevice", ACCOUNT_TYPE); 1478 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(account, 1479 OPTIONS_BUNDLE, 1480 mActivity, 1481 null /* callback */, 1482 null /* handler */); 1483 1484 futureBundle.getResult(); 1485 } 1486 1487 /** 1488 * Tests the setting of lastAuthenticatedTime on confirmCredentials being 1489 * successful. 1490 */ testLastAuthenticatedTimeAfterConfirmCredentialsSuccess()1491 public void testLastAuthenticatedTimeAfterConfirmCredentialsSuccess() throws IOException, 1492 AuthenticatorException, OperationCanceledException { 1493 1494 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1495 1496 // Now this confirm credentials call returns true, which in turn 1497 // should update the last authenticated timestamp. 1498 Bundle result = am.confirmCredentials(ACCOUNT, 1499 OPTIONS_BUNDLE, /* options */ 1500 null, /* activity */ 1501 null /* callback */, 1502 null /* handler */).getResult(); 1503 long confirmedCredTime = result.getLong( 1504 AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1); 1505 assertTrue(confirmedCredTime > accountAddTime); 1506 } 1507 1508 /** 1509 * Tests the setting of lastAuthenticatedTime on updateCredentials being 1510 * successful. 1511 */ testLastAuthenticatedTimeAfterUpdateCredentialsSuccess()1512 public void testLastAuthenticatedTimeAfterUpdateCredentialsSuccess() throws IOException, 1513 AuthenticatorException, OperationCanceledException { 1514 1515 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1516 1517 am.updateCredentials(ACCOUNT, 1518 AUTH_TOKEN_TYPE, 1519 OPTIONS_BUNDLE, 1520 mActivity, 1521 null /* callback */, 1522 null /* handler */).getResult(); 1523 long updateCredTime = getLastAuthenticatedTime(ACCOUNT); 1524 assertTrue(updateCredTime > accountAddTime); 1525 } 1526 1527 /** 1528 * LastAuthenticatedTime on setPassword should not be disturbed. 1529 */ testLastAuthenticatedTimeAfterSetPassword()1530 public void testLastAuthenticatedTimeAfterSetPassword() throws IOException, 1531 AuthenticatorException, OperationCanceledException { 1532 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1533 mockAuthenticator.callSetPassword(); 1534 long setPasswordTime = getLastAuthenticatedTime(ACCOUNT); 1535 assertTrue(setPasswordTime == accountAddTime); 1536 } 1537 1538 /** 1539 * Test confirmCredentials() with callback 1540 */ testConfirmCredentialsWithCallbackAndHandler()1541 public void testConfirmCredentialsWithCallbackAndHandler() { 1542 1543 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1544 1545 testConfirmCredentialsWithCallbackAndHandler(null /* handler */); 1546 testConfirmCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1547 } 1548 testConfirmCredentialsWithCallbackAndHandler(Handler handler)1549 private void testConfirmCredentialsWithCallbackAndHandler(Handler handler) { 1550 final CountDownLatch latch = new CountDownLatch(1); 1551 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1552 @Override 1553 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1554 1555 Bundle resultBundle = null; 1556 try { 1557 resultBundle = bundleFuture.getResult(); 1558 1559 // Assert returned result 1560 validateCredentials(); 1561 1562 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 1563 } catch (OperationCanceledException e) { 1564 fail("should not throw an OperationCanceledException"); 1565 } catch (IOException e) { 1566 fail("should not throw an IOException"); 1567 } catch (AuthenticatorException e) { 1568 fail("should not throw an AuthenticatorException"); 1569 } 1570 finally { 1571 latch.countDown(); 1572 } 1573 } 1574 }; 1575 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT, 1576 OPTIONS_BUNDLE, 1577 mActivity, 1578 callback, 1579 handler); 1580 // Wait with timeout for the callback to do its work 1581 try { 1582 latch.await(3 * LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1583 } catch (InterruptedException e) { 1584 fail("should not throw an InterruptedException"); 1585 } 1586 } 1587 1588 /** 1589 * Test updateCredentials() 1590 */ testUpdateCredentials()1591 public void testUpdateCredentials() throws IOException, AuthenticatorException, 1592 OperationCanceledException { 1593 1594 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1595 1596 AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT, 1597 AUTH_TOKEN_TYPE, 1598 OPTIONS_BUNDLE, 1599 mActivity, 1600 null /* callback*/, 1601 null /* handler */); 1602 1603 Bundle result = futureBundle.getResult(); 1604 1605 validateAccountAndNoAuthTokenResult(result); 1606 1607 // Assert returned result 1608 validateCredentials(); 1609 } 1610 1611 /** 1612 * Test updateCredentials() with callback and handler 1613 */ testUpdateCredentialsWithCallbackAndHandler()1614 public void testUpdateCredentialsWithCallbackAndHandler() throws IOException, 1615 AuthenticatorException, OperationCanceledException { 1616 1617 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1618 1619 testUpdateCredentialsWithCallbackAndHandler(null /* handler */); 1620 testUpdateCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1621 } 1622 testUpdateCredentialsWithCallbackAndHandler(Handler handler)1623 private void testUpdateCredentialsWithCallbackAndHandler(Handler handler) throws IOException, 1624 AuthenticatorException, OperationCanceledException { 1625 1626 final CountDownLatch latch = new CountDownLatch(1); 1627 1628 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1629 @Override 1630 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1631 1632 Bundle resultBundle = null; 1633 try { 1634 resultBundle = bundleFuture.getResult(); 1635 1636 // Assert returned result 1637 validateCredentials(); 1638 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 1639 1640 } catch (OperationCanceledException e) { 1641 fail("should not throw an OperationCanceledException"); 1642 } catch (IOException e) { 1643 fail("should not throw an IOException"); 1644 } catch (AuthenticatorException e) { 1645 fail("should not throw an AuthenticatorException"); 1646 } 1647 finally { 1648 latch.countDown(); 1649 } 1650 } 1651 }; 1652 1653 AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT, 1654 AUTH_TOKEN_TYPE, 1655 OPTIONS_BUNDLE, 1656 mActivity, 1657 callback, 1658 handler); 1659 1660 futureBundle.getResult(); 1661 1662 // Wait with timeout for the callback to do its work 1663 try { 1664 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1665 } catch (InterruptedException e) { 1666 fail("should not throw an InterruptedException"); 1667 } 1668 } 1669 1670 /** 1671 * Test editProperties() 1672 */ testEditProperties()1673 public void testEditProperties() throws IOException, AuthenticatorException, 1674 OperationCanceledException { 1675 1676 AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE, 1677 mActivity, 1678 null /* callback */, 1679 null /* handler*/); 1680 1681 Bundle result = futureBundle.getResult(); 1682 1683 validateAccountAndNoAuthTokenResult(result); 1684 1685 // Assert returned result 1686 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 1687 } 1688 1689 /** 1690 * Test editProperties() with callback and handler 1691 */ testEditPropertiesWithCallbackAndHandler()1692 public void testEditPropertiesWithCallbackAndHandler() { 1693 testEditPropertiesWithCallbackAndHandler(null /* handler */); 1694 testEditPropertiesWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1695 } 1696 testEditPropertiesWithCallbackAndHandler(Handler handler)1697 private void testEditPropertiesWithCallbackAndHandler(Handler handler) { 1698 final CountDownLatch latch = new CountDownLatch(1); 1699 1700 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1701 @Override 1702 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1703 try { 1704 // Assert returned result 1705 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 1706 } 1707 finally { 1708 latch.countDown(); 1709 } 1710 } 1711 }; 1712 1713 AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE, 1714 mActivity, 1715 callback, 1716 handler); 1717 1718 // Wait with timeout for the callback to do its work 1719 try { 1720 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1721 } catch (InterruptedException e) { 1722 fail("should not throw an InterruptedException"); 1723 } 1724 } 1725 1726 /** 1727 * Test addOnAccountsUpdatedListener() with handler 1728 */ testAddOnAccountsUpdatedListenerWithHandler()1729 public void testAddOnAccountsUpdatedListenerWithHandler() throws IOException, 1730 AuthenticatorException, OperationCanceledException { 1731 1732 testAddOnAccountsUpdatedListenerWithHandler(null /* handler */, 1733 false /* updateImmediately */); 1734 1735 // Need to cleanup intermediate state 1736 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1737 AccountManager.KEY_BOOLEAN_RESULT)); 1738 1739 testAddOnAccountsUpdatedListenerWithHandler(null /* handler */, 1740 true /* updateImmediately */); 1741 1742 // Need to cleanup intermediate state 1743 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1744 AccountManager.KEY_BOOLEAN_RESULT)); 1745 1746 testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()), 1747 false /* updateImmediately */); 1748 1749 // Need to cleanup intermediate state 1750 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1751 AccountManager.KEY_BOOLEAN_RESULT)); 1752 1753 testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()), 1754 true /* updateImmediately */); 1755 } 1756 testAddOnAccountsUpdatedListenerWithHandler(Handler handler, boolean updateImmediately)1757 private void testAddOnAccountsUpdatedListenerWithHandler(Handler handler, 1758 boolean updateImmediately) { 1759 1760 final CountDownLatch latch = new CountDownLatch(1); 1761 1762 OnAccountsUpdateListener listener = new OnAccountsUpdateListener() { 1763 @Override 1764 public void onAccountsUpdated(Account[] accounts) { 1765 latch.countDown(); 1766 } 1767 }; 1768 1769 // Add a listener 1770 am.addOnAccountsUpdatedListener(listener, 1771 handler, 1772 updateImmediately); 1773 1774 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1775 1776 // Wait with timeout for the callback to do its work 1777 try { 1778 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1779 } catch (InterruptedException e) { 1780 fail("should not throw an InterruptedException"); 1781 } 1782 1783 // Cleanup 1784 am.removeOnAccountsUpdatedListener(listener); 1785 } 1786 1787 /** 1788 * Test removeOnAccountsUpdatedListener() with handler 1789 */ testRemoveOnAccountsUpdatedListener()1790 public void testRemoveOnAccountsUpdatedListener() throws IOException, AuthenticatorException, 1791 OperationCanceledException { 1792 1793 testRemoveOnAccountsUpdatedListenerWithHandler(null /* handler */); 1794 1795 // Need to cleanup intermediate state 1796 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1797 AccountManager.KEY_BOOLEAN_RESULT)); 1798 1799 testRemoveOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper())); 1800 } 1801 testRemoveOnAccountsUpdatedListenerWithHandler(Handler handler)1802 private void testRemoveOnAccountsUpdatedListenerWithHandler(Handler handler) { 1803 final CountDownLatch latch = new CountDownLatch(1); 1804 1805 OnAccountsUpdateListener listener = new OnAccountsUpdateListener() { 1806 @Override 1807 public void onAccountsUpdated(Account[] accounts) { 1808 fail("should not be called"); 1809 } 1810 }; 1811 1812 // First add a listener 1813 am.addOnAccountsUpdatedListener(listener, 1814 handler, 1815 false /* updateImmediately */); 1816 1817 // Then remove the listener 1818 am.removeOnAccountsUpdatedListener(listener); 1819 1820 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1821 1822 // Wait with timeout for the callback to do its work 1823 try { 1824 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1825 } catch (InterruptedException e) { 1826 fail("should not throw an InterruptedException"); 1827 } 1828 } 1829 1830 /** 1831 * Test hasFeature 1832 */ testHasFeature()1833 public void testHasFeature() 1834 throws IOException, AuthenticatorException, OperationCanceledException { 1835 1836 assertHasFeature(null /* handler */); 1837 assertHasFeature(new Handler(Looper.getMainLooper())); 1838 1839 assertHasFeatureWithCallback(null /* handler */); 1840 assertHasFeatureWithCallback(new Handler(Looper.getMainLooper())); 1841 } 1842 assertHasFeature(Handler handler)1843 private void assertHasFeature(Handler handler) 1844 throws IOException, AuthenticatorException, OperationCanceledException { 1845 Bundle resultBundle = addAccount(am, 1846 ACCOUNT_TYPE, 1847 AUTH_TOKEN_TYPE, 1848 REQUIRED_FEATURES, 1849 OPTIONS_BUNDLE, 1850 mActivity, 1851 null /* callback */, 1852 null /* handler */); 1853 1854 // Assert parameters has been passed correctly 1855 validateAccountAndAuthTokenType(); 1856 validateFeatures(); 1857 1858 AccountManagerFuture<Boolean> booleanFuture = am.hasFeatures(ACCOUNT, 1859 new String[]{FEATURE_1}, 1860 null /* callback */, 1861 handler); 1862 assertTrue(booleanFuture.getResult()); 1863 1864 booleanFuture = am.hasFeatures(ACCOUNT, 1865 new String[]{FEATURE_2}, 1866 null /* callback */, 1867 handler); 1868 assertTrue(booleanFuture.getResult()); 1869 1870 booleanFuture = am.hasFeatures(ACCOUNT, 1871 new String[]{FEATURE_1, FEATURE_2}, 1872 null /* callback */, 1873 handler); 1874 assertTrue(booleanFuture.getResult()); 1875 1876 booleanFuture = am.hasFeatures(ACCOUNT, 1877 new String[]{NON_EXISTING_FEATURE}, 1878 null /* callback */, 1879 handler); 1880 assertFalse(booleanFuture.getResult()); 1881 1882 booleanFuture = am.hasFeatures(ACCOUNT, 1883 new String[]{NON_EXISTING_FEATURE, FEATURE_1}, 1884 null /* callback */, 1885 handler); 1886 assertFalse(booleanFuture.getResult()); 1887 1888 booleanFuture = am.hasFeatures(ACCOUNT, 1889 new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2}, 1890 null /* callback */, 1891 handler); 1892 assertFalse(booleanFuture.getResult()); 1893 } 1894 getAssertTrueCallback(final CountDownLatch latch)1895 private AccountManagerCallback<Boolean> getAssertTrueCallback(final CountDownLatch latch) { 1896 return new AccountManagerCallback<Boolean>() { 1897 @Override 1898 public void run(AccountManagerFuture<Boolean> booleanFuture) { 1899 try { 1900 // Assert returned result should be TRUE 1901 assertTrue(booleanFuture.getResult()); 1902 } catch (Exception e) { 1903 fail("Exception: " + e); 1904 } finally { 1905 latch.countDown(); 1906 } 1907 } 1908 }; 1909 } 1910 1911 private AccountManagerCallback<Boolean> getAssertFalseCallback(final CountDownLatch latch) { 1912 return new AccountManagerCallback<Boolean>() { 1913 @Override 1914 public void run(AccountManagerFuture<Boolean> booleanFuture) { 1915 try { 1916 // Assert returned result should be FALSE 1917 assertFalse(booleanFuture.getResult()); 1918 } catch (Exception e) { 1919 fail("Exception: " + e); 1920 } finally { 1921 latch.countDown(); 1922 } 1923 } 1924 }; 1925 } 1926 1927 private void waitForLatch(final CountDownLatch latch) { 1928 // Wait with timeout for the callback to do its work 1929 try { 1930 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1931 } catch (InterruptedException e) { 1932 fail("should not throw an InterruptedException"); 1933 } 1934 } 1935 1936 private void assertHasFeatureWithCallback(Handler handler) 1937 throws IOException, AuthenticatorException, OperationCanceledException { 1938 Bundle resultBundle = addAccount(am, 1939 ACCOUNT_TYPE, 1940 AUTH_TOKEN_TYPE, 1941 REQUIRED_FEATURES, 1942 OPTIONS_BUNDLE, 1943 mActivity, 1944 null /* callback */, 1945 null /* handler */); 1946 1947 // Assert parameters has been passed correctly 1948 validateAccountAndAuthTokenType(); 1949 validateFeatures(); 1950 1951 CountDownLatch latch = new CountDownLatch(1); 1952 am.hasFeatures(ACCOUNT, 1953 new String[]{FEATURE_1}, 1954 getAssertTrueCallback(latch), 1955 handler); 1956 waitForLatch(latch); 1957 1958 latch = new CountDownLatch(1); 1959 am.hasFeatures(ACCOUNT, 1960 new String[]{FEATURE_2}, 1961 getAssertTrueCallback(latch), 1962 handler); 1963 waitForLatch(latch); 1964 1965 latch = new CountDownLatch(1); 1966 am.hasFeatures(ACCOUNT, 1967 new String[]{FEATURE_1, FEATURE_2}, 1968 getAssertTrueCallback(latch), 1969 handler); 1970 waitForLatch(latch); 1971 1972 latch = new CountDownLatch(1); 1973 am.hasFeatures(ACCOUNT, 1974 new String[]{NON_EXISTING_FEATURE}, 1975 getAssertFalseCallback(latch), 1976 handler); 1977 waitForLatch(latch); 1978 1979 latch = new CountDownLatch(1); 1980 am.hasFeatures(ACCOUNT, 1981 new String[]{NON_EXISTING_FEATURE, FEATURE_1}, 1982 getAssertFalseCallback(latch), 1983 handler); 1984 waitForLatch(latch); 1985 1986 latch = new CountDownLatch(1); 1987 am.hasFeatures(ACCOUNT, 1988 new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2}, 1989 getAssertFalseCallback(latch), 1990 handler); 1991 waitForLatch(latch); 1992 } 1993 1994 private long getLastAuthenticatedTime(Account account) throws OperationCanceledException, 1995 AuthenticatorException, IOException { 1996 Bundle options = new Bundle(); 1997 options.putBoolean(MockAccountAuthenticator.KEY_RETURN_INTENT, true); 1998 // Not really confirming, but a way to get last authenticated timestamp 1999 Bundle result = am.confirmCredentials(account, 2000 options,// OPTIONS_BUNDLE, 2001 null, /* activity */ 2002 null /* callback */, 2003 null /* handler */).getResult(); 2004 return result.getLong( 2005 AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1); 2006 } 2007 2008 private long addAccountAndReturnAccountAddedTime(Account account, String password) 2009 throws OperationCanceledException, AuthenticatorException, IOException { 2010 addAccount(am, 2011 ACCOUNT_TYPE, 2012 AUTH_TOKEN_TYPE, 2013 REQUIRED_FEATURES, 2014 OPTIONS_BUNDLE, 2015 mActivity, 2016 null /* callback */, 2017 null /* handler */); 2018 return getLastAuthenticatedTime(account); 2019 } 2020 2021 /** 2022 * Tests that AccountManagerService is properly caching data. 2023 */ 2024 public void testGetsAreCached() { 2025 2026 // Add an account, 2027 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT)); 2028 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 2029 2030 // Then verify that we don't hit disk retrieving it, 2031 StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 2032 try { 2033 StrictMode.setThreadPolicy( 2034 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build()); 2035 Account[] accounts = am.getAccounts(); 2036 assertNotNull(accounts); 2037 assertTrue(accounts.length > 0); 2038 } finally { 2039 StrictMode.setThreadPolicy(oldPolicy); 2040 } 2041 } 2042 2043 } 2044