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