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.accounts.cts.common.Fixtures; 28 import android.app.Activity; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.os.Binder; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.IBinder; 36 import android.os.Looper; 37 import android.os.StrictMode; 38 import android.platform.test.annotations.AppModeFull; 39 import android.platform.test.annotations.Presubmit; 40 import android.test.ActivityInstrumentationTestCase2; 41 42 import java.io.IOException; 43 import java.lang.Math; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.HashMap; 47 import java.util.Map; 48 import java.util.concurrent.CountDownLatch; 49 import java.util.concurrent.TimeUnit; 50 import java.util.concurrent.atomic.AtomicReference; 51 52 /** 53 * You can run those unit tests with the following command line: 54 * 55 * adb shell am instrument 56 * -e debug false -w 57 * -e class android.accounts.cts.AccountManagerTest 58 * android.accounts.cts/android.support.test.runner.AndroidJUnitRunner 59 */ 60 public class AccountManagerTest extends ActivityInstrumentationTestCase2<AccountDummyActivity> { 61 62 public static final String ACCOUNT_NAME = "android.accounts.cts.account.name"; 63 public static final String ACCOUNT_NEW_NAME = "android.accounts.cts.account.name.rename"; 64 public static final String ACCOUNT_NAME_OTHER = "android.accounts.cts.account.name.other"; 65 66 public static final String ACCOUNT_TYPE = "android.accounts.cts.account.type"; 67 public static final String ACCOUNT_TYPE_CUSTOM = "android.accounts.cts.custom.account.type"; 68 public static final String ACCOUNT_TYPE_ABSENT = "android.accounts.cts.account.type.absent"; 69 70 public static final String ACCOUNT_PASSWORD = "android.accounts.cts.account.password"; 71 72 public static final String ACCOUNT_STATUS_TOKEN = "android.accounts.cts.account.status.token"; 73 74 public static final String AUTH_TOKEN_TYPE = "mockAuthTokenType"; 75 public static final String AUTH_EXPIRING_TOKEN_TYPE = "mockAuthExpiringTokenType"; 76 public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel"; 77 public static final long AUTH_TOKEN_DURATION_MILLIS = 10000L; // Ten seconds. 78 79 public static final String FEATURE_1 = "feature.1"; 80 public static final String FEATURE_2 = "feature.2"; 81 public static final String NON_EXISTING_FEATURE = "feature.3"; 82 83 public static final String OPTION_NAME_1 = "option.name.1"; 84 public static final String OPTION_VALUE_1 = "option.value.1"; 85 86 public static final String OPTION_NAME_2 = "option.name.2"; 87 public static final String OPTION_VALUE_2 = "option.value.2"; 88 89 public static final String[] REQUIRED_FEATURES = new String[] { FEATURE_1, FEATURE_2 }; 90 91 public static final Bundle OPTIONS_BUNDLE = new Bundle(); 92 93 public static final Bundle USERDATA_BUNDLE = new Bundle(); 94 95 public static final String USERDATA_NAME_1 = "user.data.name.1"; 96 public static final String USERDATA_NAME_2 = "user.data.name.2"; 97 public static final String USERDATA_VALUE_1 = "user.data.value.1"; 98 public static final String USERDATA_VALUE_2 = "user.data.value.2"; 99 100 public static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE); 101 public static final Account ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API = new Account( 102 MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE); 103 public static final Account ACCOUNT_FOR_DEFAULT_IMPL = new Account( 104 MockAccountAuthenticator.ACCOUNT_NAME_FOR_DEFAULT_IMPL, ACCOUNT_TYPE); 105 public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE); 106 107 public static final Account CUSTOM_TOKEN_ACCOUNT = 108 new Account(ACCOUNT_NAME,ACCOUNT_TYPE_CUSTOM); 109 110 // Installed packages to test visibility API. 111 public static final String PACKAGE_NAME_1 = "android.accounts.cts.unaffiliated"; 112 public static final String PACKAGE_NAME_PRIVILEGED = "android.accounts.cts"; // authenticator 113 114 public static final Bundle SESSION_BUNDLE = new Bundle(); 115 public static final String SESSION_DATA_NAME_1 = "session.data.name.1"; 116 public static final String SESSION_DATA_VALUE_1 = "session.data.value.1"; 117 118 public static final String ERROR_MESSAGE = "android.accounts.cts.account.error.message"; 119 120 public static final String KEY_CIPHER = "cipher"; 121 public static final String KEY_MAC = "mac"; 122 123 private static MockAccountAuthenticator mockAuthenticator; 124 private static final int LATCH_TIMEOUT_MS = 1000; 125 private static AccountManager am; 126 127 public synchronized static MockAccountAuthenticator getMockAuthenticator(Context context) { 128 if (null == mockAuthenticator) { 129 mockAuthenticator = new MockAccountAuthenticator(context); 130 } 131 return mockAuthenticator; 132 } 133 134 private Activity mActivity; 135 private Context mContext; 136 137 public AccountManagerTest() { 138 super(AccountDummyActivity.class); 139 } 140 141 @Override 142 public void setUp() throws Exception { 143 super.setUp(); 144 mActivity = getActivity(); 145 mContext = getInstrumentation().getTargetContext(); 146 147 OPTIONS_BUNDLE.putString(OPTION_NAME_1, OPTION_VALUE_1); 148 OPTIONS_BUNDLE.putString(OPTION_NAME_2, OPTION_VALUE_2); 149 150 USERDATA_BUNDLE.putString(USERDATA_NAME_1, USERDATA_VALUE_1); 151 152 SESSION_BUNDLE.putString(SESSION_DATA_NAME_1, SESSION_DATA_VALUE_1); 153 SESSION_BUNDLE.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); 154 155 getMockAuthenticator(mContext); 156 157 am = AccountManager.get(mContext); 158 } 159 160 @Override 161 public void tearDown() throws Exception, AuthenticatorException, OperationCanceledException { 162 mockAuthenticator.clearData(); 163 164 // Need to clean up created account 165 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 166 AccountManager.KEY_BOOLEAN_RESULT)); 167 assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean( 168 AccountManager.KEY_BOOLEAN_RESULT)); 169 170 // Clean out any other accounts added during the tests. 171 Account[] ctsAccounts = am.getAccountsByType(ACCOUNT_TYPE); 172 Account[] ctsCustomAccounts = am.getAccountsByType(ACCOUNT_TYPE_CUSTOM); 173 ArrayList<Account> accounts = new ArrayList<>(Arrays.asList(ctsAccounts)); 174 accounts.addAll(Arrays.asList(ctsCustomAccounts)); 175 for (Account ctsAccount : accounts) { 176 removeAccount(am, ctsAccount, mActivity, null /* callback */); 177 } 178 179 // need to clean up the authenticator cached data 180 mockAuthenticator.clearData(); 181 182 super.tearDown(); 183 } 184 185 interface TokenFetcher { 186 public Bundle fetch(String tokenType) 187 throws OperationCanceledException, AuthenticatorException, IOException; 188 public Account getAccount(); 189 } 190 191 private void validateSuccessfulTokenFetchingLifecycle(TokenFetcher fetcher, String tokenType) 192 throws OperationCanceledException, AuthenticatorException, IOException { 193 Account account = fetcher.getAccount(); 194 Bundle expected = new Bundle(); 195 expected.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 196 expected.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 197 198 // First fetch. 199 Bundle actual = fetcher.fetch(tokenType); 200 assertTrue(mockAuthenticator.isRecentlyCalled()); 201 validateAccountAndAuthTokenResult(expected, actual); 202 203 /* 204 * On the second fetch the cache will be populated if we are using a authenticator with 205 * customTokens=false or we are using a scope that will cause the authenticator to set an 206 * expiration time (and that expiration time hasn't been reached). 207 */ 208 actual = fetcher.fetch(tokenType); 209 210 boolean isCachingExpected = 211 ACCOUNT_TYPE.equals(account.type) || AUTH_EXPIRING_TOKEN_TYPE.equals(tokenType); 212 assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled()); 213 validateAccountAndAuthTokenResult(expected, actual); 214 215 try { 216 // Delay further execution until expiring tokens can actually expire. 217 Thread.sleep(mockAuthenticator.getTokenDurationMillis() + 50L); 218 } catch (InterruptedException e) { 219 throw new RuntimeException(e); 220 } 221 222 /* 223 * With the time shift above, the third request will result in cache hits only from 224 * customToken=false authenticators. 225 */ 226 actual = fetcher.fetch(tokenType); 227 isCachingExpected = ACCOUNT_TYPE.equals(account.type); 228 assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled()); 229 validateAccountAndAuthTokenResult(expected, actual); 230 231 // invalidate token 232 String token = actual.getString(AccountManager.KEY_AUTHTOKEN); 233 am.invalidateAuthToken(account.type, token); 234 235 /* 236 * Upon invalidating the token, the cache should be clear regardless of authenticator. 237 */ 238 actual = fetcher.fetch(tokenType); 239 assertTrue(mockAuthenticator.isRecentlyCalled()); 240 validateAccountAndAuthTokenResult(expected, actual); 241 } 242 243 private void validateAccountAndAuthTokenResult(Bundle actual) { 244 assertEquals( 245 ACCOUNT.name, 246 actual.get(AccountManager.KEY_ACCOUNT_NAME)); 247 assertEquals( 248 ACCOUNT.type, 249 actual.get(AccountManager.KEY_ACCOUNT_TYPE)); 250 assertEquals( 251 mockAuthenticator.getLastTokenServed(), 252 actual.get(AccountManager.KEY_AUTHTOKEN)); 253 } 254 255 private void validateAccountAndAuthTokenResult(Bundle expected, Bundle actual) { 256 assertEquals( 257 expected.get(AccountManager.KEY_ACCOUNT_NAME), 258 actual.get(AccountManager.KEY_ACCOUNT_NAME)); 259 assertEquals( 260 expected.get(AccountManager.KEY_ACCOUNT_TYPE), 261 actual.get(AccountManager.KEY_ACCOUNT_TYPE)); 262 assertEquals( 263 mockAuthenticator.getLastTokenServed(), 264 actual.get(AccountManager.KEY_AUTHTOKEN)); 265 } 266 267 private void validateAccountAndNoAuthTokenResult(Bundle result) { 268 assertEquals(ACCOUNT_NAME, result.get(AccountManager.KEY_ACCOUNT_NAME)); 269 assertEquals(ACCOUNT_TYPE, result.get(AccountManager.KEY_ACCOUNT_TYPE)); 270 assertNull(result.get(AccountManager.KEY_AUTHTOKEN)); 271 } 272 273 private void validateNullResult(Bundle resultBundle) { 274 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_NAME)); 275 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_TYPE)); 276 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 277 } 278 279 private void validateAccountAndAuthTokenType() { 280 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 281 assertEquals(AUTH_TOKEN_TYPE, mockAuthenticator.getAuthTokenType()); 282 } 283 284 private void validateFeatures() { 285 assertEquals(REQUIRED_FEATURES[0], mockAuthenticator.getRequiredFeatures()[0]); 286 assertEquals(REQUIRED_FEATURES[1], mockAuthenticator.getRequiredFeatures()[1]); 287 } 288 289 private void validateOptions(Bundle expectedOptions, Bundle actualOptions) { 290 // In ICS AccountManager may add options to indicate the caller id. 291 // We only validate that the passed in options are present in the actual ones 292 if (expectedOptions != null) { 293 assertNotNull(actualOptions); 294 assertEquals(expectedOptions.get(OPTION_NAME_1), actualOptions.get(OPTION_NAME_1)); 295 assertEquals(expectedOptions.get(OPTION_NAME_2), actualOptions.get(OPTION_NAME_2)); 296 } 297 } 298 299 private void validateSystemOptions(Bundle options) { 300 assertNotNull(options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME)); 301 assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID)); 302 assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID)); 303 } 304 305 private void validateCredentials() { 306 assertEquals(ACCOUNT, mockAuthenticator.getAccount()); 307 } 308 309 private int getAccountsCount() { 310 Account[] accounts = am.getAccounts(); 311 assertNotNull(accounts); 312 return accounts.length; 313 } 314 315 private Bundle addAccount(AccountManager am, String accountType, String authTokenType, 316 String[] requiredFeatures, Bundle options, Activity activity, 317 AccountManagerCallback<Bundle> callback, Handler handler) throws 318 IOException, AuthenticatorException, OperationCanceledException { 319 320 AccountManagerFuture<Bundle> futureBundle = am.addAccount( 321 accountType, 322 authTokenType, 323 requiredFeatures, 324 options, 325 activity, 326 callback, 327 handler); 328 329 Bundle resultBundle = futureBundle.getResult(); 330 assertTrue(futureBundle.isDone()); 331 assertNotNull(resultBundle); 332 333 return resultBundle; 334 } 335 336 private Account renameAccount(AccountManager am, Account account, String newName) 337 throws OperationCanceledException, AuthenticatorException, IOException { 338 AccountManagerFuture<Account> futureAccount = am.renameAccount( 339 account, newName, null /* callback */, null /* handler */); 340 Account renamedAccount = futureAccount.getResult(); 341 assertTrue(futureAccount.isDone()); 342 assertNotNull(renamedAccount); 343 return renamedAccount; 344 } 345 346 private boolean removeAccount(AccountManager am, Account account, 347 AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException, 348 OperationCanceledException { 349 AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account, 350 callback, 351 null /* handler */); 352 Boolean resultBoolean = futureBoolean.getResult(); 353 assertTrue(futureBoolean.isDone()); 354 355 return resultBoolean; 356 } 357 358 private Bundle removeAccountWithIntentLaunch(AccountManager am, Account account, 359 Activity activity, AccountManagerCallback<Bundle> callback) throws IOException, 360 AuthenticatorException, OperationCanceledException { 361 362 AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account, 363 activity, 364 callback, 365 null /* handler */); 366 Bundle resultBundle = futureBundle.getResult(); 367 assertTrue(futureBundle.isDone()); 368 369 return resultBundle; 370 } 371 372 private Bundle removeAccount(AccountManager am, Account account, Activity activity, 373 AccountManagerCallback<Bundle> callback) throws IOException, AuthenticatorException, 374 OperationCanceledException { 375 376 AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account, 377 activity, 378 callback, 379 null /* handler */); 380 Bundle resultBundle = futureBundle.getResult(); 381 assertTrue(futureBundle.isDone()); 382 383 return resultBundle; 384 } 385 386 private boolean removeAccountExplicitly(AccountManager am, Account account) { 387 return am.removeAccountExplicitly(account); 388 } 389 390 private void addAccountExplicitly(Account account, String password, Bundle userdata) { 391 assertTrue(am.addAccountExplicitly(account, password, userdata)); 392 } 393 394 private Bundle getAuthTokenByFeature(String[] features, Activity activity) 395 throws IOException, AuthenticatorException, OperationCanceledException { 396 397 AccountManagerFuture<Bundle> futureBundle = am.getAuthTokenByFeatures(ACCOUNT_TYPE, 398 AUTH_TOKEN_TYPE, 399 features, 400 activity, 401 OPTIONS_BUNDLE, 402 OPTIONS_BUNDLE, 403 null /* no callback */, 404 null /* no handler */ 405 ); 406 407 Bundle resultBundle = futureBundle.getResult(); 408 409 assertTrue(futureBundle.isDone()); 410 assertNotNull(resultBundle); 411 412 return resultBundle; 413 } 414 415 private boolean isAccountPresent(Account[] accounts, Account accountToCheck) { 416 if (null == accounts || null == accountToCheck) { 417 return false; 418 } 419 boolean result = false; 420 int length = accounts.length; 421 for (int n=0; n<length; n++) { 422 if(accountToCheck.equals(accounts[n])) { 423 result = true; 424 break; 425 } 426 } 427 return result; 428 } 429 430 /** 431 * Test singleton 432 */ 433 public void testGet() { 434 assertNotNull(AccountManager.get(mContext)); 435 } 436 437 /** 438 * Test creation of intent 439 */ 440 public void testNewChooseAccountIntent() { 441 Intent intent = AccountManager.newChooseAccountIntent(null, null, null, 442 null, null, 443 null, null); 444 assertNotNull(intent); 445 } 446 447 /** 448 * Test creation of intent 449 */ 450 public void testNewChooseAccountIntentDepracated() { 451 Intent intent = AccountManager.newChooseAccountIntent(null, null, null, false, 452 null, null, 453 null, null); 454 assertNotNull(intent); 455 } 456 457 /** 458 * Test a basic addAccount() 459 */ 460 public void testAddAccount() throws IOException, AuthenticatorException, 461 OperationCanceledException { 462 463 Bundle resultBundle = addAccount(am, 464 ACCOUNT_TYPE, 465 AUTH_TOKEN_TYPE, 466 REQUIRED_FEATURES, 467 OPTIONS_BUNDLE, 468 mActivity, 469 null /* callback */, 470 null /* handler */); 471 472 // Assert parameters has been passed correctly 473 validateAccountAndAuthTokenType(); 474 validateFeatures(); 475 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 476 validateSystemOptions(mockAuthenticator.mOptionsAddAccount); 477 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 478 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 479 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 480 481 // Assert returned result 482 validateAccountAndNoAuthTokenResult(resultBundle); 483 } 484 485 /** 486 * Test addAccount() with callback and handler 487 */ 488 public void testAddAccountWithCallbackAndHandler() throws IOException, 489 AuthenticatorException, OperationCanceledException { 490 491 testAddAccountWithCallbackAndHandler(null /* handler */); 492 testAddAccountWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 493 } 494 495 /** 496 * Test addAccount() with no associated account authenticator 497 */ 498 public void testAddAccountWithNoAuthenticator() throws IOException, 499 AuthenticatorException, OperationCanceledException { 500 501 try { 502 AccountManagerFuture<Bundle> futureBundle = am.addAccount( 503 "nonExistingAccountType", 504 null, 505 null, 506 null, 507 null, 508 null, 509 null); 510 511 futureBundle.getResult(); 512 fail(); 513 } catch (AuthenticatorException expectedException) { 514 return; 515 } 516 } 517 518 private void testAddAccountWithCallbackAndHandler(Handler handler) throws IOException, 519 AuthenticatorException, OperationCanceledException { 520 521 final CountDownLatch latch = new CountDownLatch(1); 522 523 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 524 @Override 525 public void run(AccountManagerFuture<Bundle> bundleFuture) { 526 Bundle resultBundle = null; 527 try { 528 resultBundle = bundleFuture.getResult(); 529 } catch (OperationCanceledException e) { 530 fail("should not throw an OperationCanceledException"); 531 } catch (IOException e) { 532 fail("should not throw an IOException"); 533 } catch (AuthenticatorException e) { 534 fail("should not throw an AuthenticatorException"); 535 } 536 537 // Assert parameters has been passed correctly 538 validateAccountAndAuthTokenType(); 539 validateFeatures(); 540 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 541 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 542 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 543 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 544 545 // Assert return result 546 validateAccountAndNoAuthTokenResult(resultBundle); 547 548 latch.countDown(); 549 } 550 }; 551 552 addAccount(am, 553 ACCOUNT_TYPE, 554 AUTH_TOKEN_TYPE, 555 REQUIRED_FEATURES, 556 OPTIONS_BUNDLE, 557 mActivity, 558 callback, 559 handler); 560 561 // Wait with timeout for the callback to do its work 562 try { 563 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 564 } catch (InterruptedException e) { 565 fail("should not throw an InterruptedException"); 566 } 567 } 568 569 /** 570 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 571 */ 572 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 573 public void testAddAccountExplicitlyAndRemoveAccount() throws IOException, 574 AuthenticatorException, OperationCanceledException { 575 576 final int expectedAccountsCount = getAccountsCount(); 577 578 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 579 580 // Assert that we have one more account 581 Account[] accounts = am.getAccounts(); 582 assertNotNull(accounts); 583 assertEquals(1 + expectedAccountsCount, accounts.length); 584 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 585 // Need to clean up 586 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 587 AccountManager.KEY_BOOLEAN_RESULT)); 588 589 // and verify that we go back to the initial state 590 accounts = am.getAccounts(); 591 assertNotNull(accounts); 592 assertEquals(expectedAccountsCount, accounts.length); 593 } 594 595 /** 596 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 597 */ 598 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 599 public void testAddAccountExplicitlyAndRemoveAccountWithNewApi() throws IOException, 600 AuthenticatorException, OperationCanceledException { 601 602 final int expectedAccountsCount = getAccountsCount(); 603 604 addAccountExplicitly(ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, ACCOUNT_PASSWORD, null /* userData */); 605 606 // Assert that we have one more account 607 Account[] accounts = am.getAccounts(); 608 assertNotNull(accounts); 609 assertEquals(1 + expectedAccountsCount, accounts.length); 610 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API)); 611 // Deprecated API should not work 612 assertFalse(removeAccount(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, null /* callback */)); 613 accounts = am.getAccounts(); 614 assertNotNull(accounts); 615 assertEquals(1 + expectedAccountsCount, accounts.length); 616 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API)); 617 // Check removal of account 618 assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, mActivity, null /* callback */) 619 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)); 620 // and verify that we go back to the initial state 621 accounts = am.getAccounts(); 622 assertNotNull(accounts); 623 assertEquals(expectedAccountsCount, accounts.length); 624 } 625 626 /** 627 * Test addAccountExplicitly(), renameAccount() and removeAccount() calling 628 * into default implementations. 629 */ 630 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 631 public void testAddAccountExplicitlyAndRemoveAccountWithDefaultImpl() throws IOException, 632 AuthenticatorException, OperationCanceledException { 633 634 final int expectedAccountsCount = getAccountsCount(); 635 636 addAccountExplicitly(ACCOUNT_FOR_DEFAULT_IMPL, ACCOUNT_PASSWORD, null /* userData */); 637 638 // Assert that we have one more account 639 Account[] accounts = am.getAccounts(); 640 assertNotNull(accounts); 641 assertEquals(1 + expectedAccountsCount, accounts.length); 642 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_DEFAULT_IMPL)); 643 // Check removal of account 644 assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_DEFAULT_IMPL, mActivity, null /* callback */) 645 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)); 646 // and verify that we go back to the initial state 647 accounts = am.getAccounts(); 648 assertNotNull(accounts); 649 assertEquals(expectedAccountsCount, accounts.length); 650 } 651 652 /** 653 * Test addAccountExplicitly(), renameAccount() and removeAccount(). 654 */ 655 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 656 public void testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi() throws IOException, 657 AuthenticatorException, OperationCanceledException { 658 659 final int expectedAccountsCount = getAccountsCount(); 660 661 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 662 663 // Assert that we have one more account 664 Account[] accounts = am.getAccounts(); 665 assertNotNull(accounts); 666 assertEquals(1 + expectedAccountsCount, accounts.length); 667 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 668 // Need to clean up 669 assertTrue(removeAccount(am, ACCOUNT, null /* callback */)); 670 671 // and verify that we go back to the initial state 672 accounts = am.getAccounts(); 673 assertNotNull(accounts); 674 assertEquals(expectedAccountsCount, accounts.length); 675 } 676 677 /** 678 * Test addAccountExplicitly() and removeAccountExplictly(). 679 */ 680 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 681 public void testAddAccountExplicitlyAndRemoveAccountExplicitly() { 682 final int expectedAccountsCount = getAccountsCount(); 683 684 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 685 686 // Assert that we have one more account 687 Account[] accounts = am.getAccounts(); 688 assertNotNull(accounts); 689 assertEquals(1 + expectedAccountsCount, accounts.length); 690 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 691 // Need to clean up 692 assertTrue(removeAccountExplicitly(am, ACCOUNT)); 693 694 // and verify that we go back to the initial state 695 accounts = am.getAccounts(); 696 assertNotNull(accounts); 697 assertEquals(expectedAccountsCount, accounts.length); 698 } 699 700 /** 701 * Test updates to account visibility. 702 */ 703 @AppModeFull(reason = "The methods requires the caller to match signature with authenticator.") 704 public void testSetAccountVisibility() 705 throws IOException, AuthenticatorException, OperationCanceledException { 706 am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 707 708 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, AccountManager.VISIBILITY_VISIBLE); 709 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 710 AccountManager.VISIBILITY_VISIBLE); 711 712 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, AccountManager.VISIBILITY_NOT_VISIBLE); 713 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 714 AccountManager.VISIBILITY_NOT_VISIBLE); 715 716 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_PRIVILEGED, 717 AccountManager.VISIBILITY_VISIBLE); 718 // No changes to PACKAGE_NAME_1 719 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 720 AccountManager.VISIBILITY_NOT_VISIBLE); 721 } 722 723 /** 724 * Test updates to account visibility for authenticator package. 725 */ 726 @AppModeFull(reason = "The methods requires the caller to match signature with authenticator.") 727 public void testSetAccountVisibilityForPrivilegedPackage() 728 throws IOException, AuthenticatorException, OperationCanceledException { 729 am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 730 731 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_PRIVILEGED), 732 AccountManager.VISIBILITY_VISIBLE); 733 Map<String, Integer> visibilities = am.getPackagesAndVisibilityForAccount(ACCOUNT); 734 assertNull(visibilities.get(PACKAGE_NAME_PRIVILEGED)); // no entry in database 735 736 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_PRIVILEGED, 737 AccountManager.VISIBILITY_NOT_VISIBLE); 738 visibilities = am.getPackagesAndVisibilityForAccount(ACCOUNT); 739 // database is updated 740 assertEquals((int) visibilities.get(PACKAGE_NAME_PRIVILEGED), 741 AccountManager.VISIBILITY_NOT_VISIBLE); 742 // VISIBILITY_VISIBLE is used for Authenticator despite database entry. 743 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_PRIVILEGED), 744 AccountManager.VISIBILITY_VISIBLE); 745 } 746 747 /** 748 * Test getPackagesAndVisibilityForAccount() method. 749 */ 750 @AppModeFull(reason = "The methods requires the caller to match signature with authenticator.") 751 public void testGetPackagesAndVisibilityForAccount() 752 throws IOException, AuthenticatorException, OperationCanceledException { 753 am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 754 755 Map<String, Integer> visibilities = am.getPackagesAndVisibilityForAccount(ACCOUNT); 756 assertNull(visibilities.get(PACKAGE_NAME_1)); // no entry in database 757 758 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, AccountManager.VISIBILITY_VISIBLE); 759 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_PRIVILEGED, 760 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 761 visibilities = am.getPackagesAndVisibilityForAccount(ACCOUNT); 762 assertEquals(visibilities.size(), 2); 763 assertEquals((int) visibilities.get(PACKAGE_NAME_1), AccountManager.VISIBILITY_VISIBLE); 764 assertEquals((int) visibilities.get(PACKAGE_NAME_PRIVILEGED), 765 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 766 } 767 768 /** 769 * Test addAccountExplicitly(), setAccountVisibility() , getAccountVisibility(), and 770 * removeAccount(). 771 */ 772 @AppModeFull(reason = "The methods are for sign-up wizards associated with authenticators.") 773 public void testAddAccountExplicitlyWithVisibility() 774 throws IOException, AuthenticatorException, OperationCanceledException { 775 Map<String, Integer> visibility = new HashMap<>(); 776 visibility.put(PACKAGE_NAME_1, AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); 777 778 final int expectedAccountsCount = getAccountsCount(); 779 780 am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */, visibility); 781 782 // Assert that we have one more account 783 Account[] accounts = am.getAccounts(); 784 assertNotNull(accounts); 785 assertEquals(1 + expectedAccountsCount, accounts.length); 786 assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT)); 787 788 // Visibility values were stored. 789 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 790 AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); 791 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */) 792 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)); 793 794 // Visibility values were removed 795 Map<Account, Integer> visibilities = 796 am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_1, ACCOUNT_TYPE); 797 assertNull(visibilities.get(ACCOUNT)); 798 799 // and verify that we go back to the initial state 800 accounts = am.getAccounts(); 801 assertNotNull(accounts); 802 assertEquals(expectedAccountsCount, accounts.length); 803 } 804 805 /** 806 * Test testGetAccountsAndVisibilityForPackage(), getAccountsByTypeForPackage() methods. 807 */ 808 @AppModeFull(reason = "The methods requires the caller to match signature with authenticator.") 809 public void testGetAccountsAndVisibilityForPackage() { 810 am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */, null); 811 am.addAccountExplicitly(ACCOUNT_SAME_TYPE, ACCOUNT_PASSWORD, null /* userData */, null); 812 813 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, AccountManager.VISIBILITY_NOT_VISIBLE); 814 am.setAccountVisibility(ACCOUNT_SAME_TYPE, PACKAGE_NAME_1, 815 AccountManager.VISIBILITY_VISIBLE); 816 assertEquals(am.getAccountVisibility(ACCOUNT_SAME_TYPE, PACKAGE_NAME_1), 817 AccountManager.VISIBILITY_VISIBLE); 818 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 819 AccountManager.VISIBILITY_NOT_VISIBLE); 820 Account[] accounts = am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_1); 821 assertEquals(accounts.length, 1); // VISIBILITY_NOT_VISIBLE accounts are not returned. 822 assertEquals(accounts[0], ACCOUNT_SAME_TYPE); 823 Map<Account, Integer> visibilities = 824 am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_1, ACCOUNT_TYPE); 825 assertEquals((int) visibilities.get(ACCOUNT), AccountManager.VISIBILITY_NOT_VISIBLE); 826 827 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, 828 AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); 829 830 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 831 AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); 832 // VISIBILITY_USER_MANAGED_NOT_VISIBLE accounts are returned by getAccountsByTypeForPackage 833 assertEquals(am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_1).length, 2); 834 visibilities = am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_1, ACCOUNT_TYPE); 835 assertEquals((int) visibilities.get(ACCOUNT), 836 AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); 837 838 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, 839 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 840 841 assertEquals(am.getAccountVisibility(ACCOUNT, PACKAGE_NAME_1), 842 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 843 assertEquals(am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_1).length, 2); 844 visibilities = am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_1, ACCOUNT_TYPE); 845 assertEquals((int) visibilities.get(ACCOUNT), 846 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 847 848 am.setAccountVisibility(ACCOUNT, PACKAGE_NAME_1, AccountManager.VISIBILITY_VISIBLE); 849 850 assertEquals(am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_1).length, 2); 851 visibilities = am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_1, ACCOUNT_TYPE); 852 assertEquals((int) visibilities.get(ACCOUNT), AccountManager.VISIBILITY_VISIBLE); 853 assertEquals(am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_1).length, 2); 854 855 // VISIBILITY_USER MANAGED_NOT_VISIBLE accounts are not returned when type is null. 856 // It should be equivalent to callling am.getAccounts() which doesn't return 857 // VISIBILITY_USER MANAGED_NOT_VISIBLE accounts. 858 assertEquals(am.getAccountsByTypeForPackage(null, PACKAGE_NAME_1).length, 859 am.getAccounts().length); 860 } 861 862 /** 863 * Test checks order of accounts returned by getAccounts...(). 864 * Accounts should be grouped by type. 865 */ 866 @AppModeFull(reason = "The methods requires the caller to match signature with authenticator.") 867 public void testGetAccountsReturnedOrder() { 868 Account account_1_1 = new Account("account_z", ACCOUNT_TYPE); 869 Account account_1_2 = new Account("account_c", ACCOUNT_TYPE); 870 Account account_1_3 = new Account("account_a", ACCOUNT_TYPE); 871 872 Account account_2_1 = new Account("account_b", ACCOUNT_TYPE_CUSTOM); 873 Account account_2_2 = new Account("account_f", ACCOUNT_TYPE_CUSTOM); 874 Account account_2_3 = new Account("account_a", ACCOUNT_TYPE_CUSTOM); 875 876 am.addAccountExplicitly(account_1_1, ACCOUNT_PASSWORD, null /* userData */, null); 877 am.addAccountExplicitly(account_1_2, ACCOUNT_PASSWORD, null /* userData */, null); 878 am.addAccountExplicitly(account_2_1, ACCOUNT_PASSWORD, null /* userData */, null); 879 880 verifyAccountsGroupedByType(am.getAccounts()); 881 verifyAccountsGroupedByType(am.getAccountsByType(null)); 882 verifyAccountsGroupedByType(am.getAccountsByTypeForPackage(null, PACKAGE_NAME_1)); 883 884 am.addAccountExplicitly(account_2_2, ACCOUNT_PASSWORD, null /* userData */, null); 885 886 verifyAccountsGroupedByType(am.getAccounts()); 887 verifyAccountsGroupedByType(am.getAccountsByType(null)); 888 verifyAccountsGroupedByType(am.getAccountsByTypeForPackage(null, PACKAGE_NAME_1)); 889 890 am.addAccountExplicitly(account_1_3, ACCOUNT_PASSWORD, null /* userData */, null); 891 verifyAccountsGroupedByType(am.getAccounts()); 892 verifyAccountsGroupedByType(am.getAccountsByType(null)); 893 verifyAccountsGroupedByType(am.getAccountsByTypeForPackage(null, PACKAGE_NAME_1)); 894 895 am.addAccountExplicitly(account_2_3, ACCOUNT_PASSWORD, null /* userData */, null); 896 897 verifyAccountsGroupedByType(am.getAccounts()); 898 verifyAccountsGroupedByType(am.getAccountsByType(null)); 899 verifyAccountsGroupedByType(am.getAccountsByTypeForPackage(null, PACKAGE_NAME_1)); 900 } 901 902 /** 903 * Test setUserData() and getUserData(). 904 */ 905 public void testAccountRenameAndGetPreviousName() 906 throws OperationCanceledException, AuthenticatorException, IOException { 907 // Add a first account 908 909 boolean result = am.addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, USERDATA_BUNDLE); 910 911 assertTrue(result); 912 913 // Prior to a rename, the previous name should be null. 914 String nullName = am.getPreviousName(ACCOUNT); 915 assertNull(nullName); 916 917 final int expectedAccountsCount = getAccountsCount(); 918 919 Account renamedAccount = renameAccount(am, ACCOUNT, ACCOUNT_NEW_NAME); 920 /* 921 * Make sure that the resultant renamed account has the correct name 922 * and is associated with the correct account type. 923 */ 924 assertEquals(ACCOUNT_NEW_NAME, renamedAccount.name); 925 assertEquals(ACCOUNT.type, renamedAccount.type); 926 927 // Make sure the total number of accounts is the same. 928 Account[] accounts = am.getAccounts(); 929 assertEquals(expectedAccountsCount, accounts.length); 930 931 // Make sure the old account isn't present. 932 assertFalse(isAccountPresent(am.getAccounts(), ACCOUNT)); 933 934 // But that the new one is. 935 assertTrue(isAccountPresent(am.getAccounts(), renamedAccount)); 936 937 // Check that the UserData is still present. 938 assertEquals(USERDATA_VALUE_1, am.getUserData(renamedAccount, USERDATA_NAME_1)); 939 940 assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount)); 941 942 // Need to clean up 943 assertTrue(removeAccount(am, renamedAccount, mActivity, null /* callback */).getBoolean( 944 AccountManager.KEY_BOOLEAN_RESULT)); 945 946 } 947 948 /** 949 * Test getAccounts() and getAccountsByType() 950 */ 951 public void testGetAccountsAndGetAccountsByType() { 952 953 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT)); 954 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT_SAME_TYPE)); 955 956 final int accountsCount = getAccountsCount(); 957 958 // Add a first account 959 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 960 961 // Check that we have the new account 962 Account[] accounts = am.getAccounts(); 963 assertEquals(1 + accountsCount, accounts.length); 964 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 965 966 // Add another account 967 addAccountExplicitly(ACCOUNT_SAME_TYPE, ACCOUNT_PASSWORD, null /* userData */); 968 969 // Check that we have one more account again 970 accounts = am.getAccounts(); 971 assertEquals(2 + accountsCount, accounts.length); 972 assertEquals(true, isAccountPresent(accounts, ACCOUNT_SAME_TYPE)); 973 974 // Check if we have one from first type 975 accounts = am.getAccountsByType(ACCOUNT_TYPE); 976 assertEquals(2, accounts.length); 977 978 // Check if we dont have any account from the other type 979 accounts = am.getAccountsByType(ACCOUNT_TYPE_ABSENT); 980 assertEquals(0, accounts.length); 981 } 982 983 /** 984 * Test getAuthenticatorTypes() 985 */ 986 public void testGetAuthenticatorTypes() { 987 AuthenticatorDescription[] types = am.getAuthenticatorTypes(); 988 for(AuthenticatorDescription description: types) { 989 if (description.type.equals(ACCOUNT_TYPE)) { 990 return; 991 } 992 } 993 fail("should have found Authenticator type: " + ACCOUNT_TYPE); 994 } 995 996 /** 997 * Test setPassword() and getPassword() 998 */ 999 public void testSetAndGetAndClearPassword() { 1000 // Add a first account 1001 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1002 1003 // Check that the password is the one we defined 1004 assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT)); 1005 1006 // Clear the password and check that it is cleared 1007 am.clearPassword(ACCOUNT); 1008 assertNull(am.getPassword(ACCOUNT)); 1009 1010 // Reset the password 1011 am.setPassword(ACCOUNT, ACCOUNT_PASSWORD); 1012 1013 // Check that the password is the one we defined 1014 assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT)); 1015 } 1016 1017 /** 1018 * Test setUserData() and getUserData() 1019 */ 1020 public void testSetAndGetUserData() { 1021 // Add a first account 1022 boolean result = am.addAccountExplicitly(ACCOUNT, 1023 ACCOUNT_PASSWORD, 1024 USERDATA_BUNDLE); 1025 1026 assertTrue(result); 1027 1028 // Check that the UserData is the one we defined 1029 assertEquals(USERDATA_VALUE_1, am.getUserData(ACCOUNT, USERDATA_NAME_1)); 1030 1031 am.setUserData(ACCOUNT, USERDATA_NAME_2, USERDATA_VALUE_2); 1032 1033 // Check that the UserData is the one we defined 1034 assertEquals(USERDATA_VALUE_2, am.getUserData(ACCOUNT, USERDATA_NAME_2)); 1035 } 1036 1037 /** 1038 * Test getAccountsByTypeAndFeatures() 1039 */ 1040 public void testGetAccountsByTypeAndFeatures() throws IOException, 1041 AuthenticatorException, OperationCanceledException { 1042 1043 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1044 1045 AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures( 1046 ACCOUNT_TYPE, REQUIRED_FEATURES, null, null); 1047 1048 Account[] accounts = futureAccounts.getResult(); 1049 1050 assertNotNull(accounts); 1051 assertEquals(1, accounts.length); 1052 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 1053 1054 futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, 1055 new String[] { NON_EXISTING_FEATURE }, 1056 null /* callback*/, 1057 null /* handler */); 1058 accounts = futureAccounts.getResult(); 1059 1060 assertNotNull(accounts); 1061 assertEquals(0, accounts.length); 1062 } 1063 1064 /** 1065 * Test getAccountsByTypeAndFeatures() with callback and handler 1066 */ 1067 public void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler() throws IOException, 1068 AuthenticatorException, OperationCanceledException { 1069 1070 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1071 1072 testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(null /* handler */); 1073 testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1074 } 1075 1076 private void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(Handler handler) throws 1077 IOException, AuthenticatorException, OperationCanceledException { 1078 1079 final CountDownLatch latch1 = new CountDownLatch(1); 1080 1081 AccountManagerCallback<Account[]> callback1 = new AccountManagerCallback<Account[]>() { 1082 @Override 1083 public void run(AccountManagerFuture<Account[]> accountsFuture) { 1084 try { 1085 Account[] accounts = accountsFuture.getResult(); 1086 assertNotNull(accounts); 1087 assertEquals(1, accounts.length); 1088 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 1089 } catch (OperationCanceledException e) { 1090 fail("should not throw an OperationCanceledException"); 1091 } catch (IOException e) { 1092 fail("should not throw an IOException"); 1093 } catch (AuthenticatorException e) { 1094 fail("should not throw an AuthenticatorException"); 1095 } finally { 1096 latch1.countDown(); 1097 } 1098 } 1099 }; 1100 1101 AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures( 1102 ACCOUNT_TYPE, 1103 REQUIRED_FEATURES, 1104 callback1, 1105 handler); 1106 1107 Account[] accounts = futureAccounts.getResult(); 1108 1109 assertNotNull(accounts); 1110 assertEquals(1, accounts.length); 1111 assertEquals(true, isAccountPresent(accounts, ACCOUNT)); 1112 1113 // Wait with timeout for the callback to do its work 1114 try { 1115 latch1.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1116 } catch (InterruptedException e) { 1117 fail("should not throw an InterruptedException"); 1118 } 1119 1120 final CountDownLatch latch2 = new CountDownLatch(1); 1121 1122 AccountManagerCallback<Account[]> callback2 = new AccountManagerCallback<Account[]>() { 1123 @Override 1124 public void run(AccountManagerFuture<Account[]> accountsFuture) { 1125 try { 1126 Account[] accounts = accountsFuture.getResult(); 1127 assertNotNull(accounts); 1128 assertEquals(0, accounts.length); 1129 } catch (OperationCanceledException e) { 1130 fail("should not throw an OperationCanceledException"); 1131 } catch (IOException e) { 1132 fail("should not throw an IOException"); 1133 } catch (AuthenticatorException e) { 1134 fail("should not throw an AuthenticatorException"); 1135 } finally { 1136 latch2.countDown(); 1137 } 1138 } 1139 }; 1140 1141 accounts = null; 1142 1143 futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, 1144 new String[] { NON_EXISTING_FEATURE }, 1145 callback2, 1146 handler); 1147 1148 accounts = futureAccounts.getResult(); 1149 assertNotNull(accounts); 1150 assertEquals(0, accounts.length); 1151 1152 // Wait with timeout for the callback to do its work 1153 try { 1154 latch2.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1155 } catch (InterruptedException e) { 1156 fail("should not throw an InterruptedException"); 1157 } 1158 } 1159 1160 /** 1161 * Test setAuthToken() and peekAuthToken() 1162 */ 1163 public void testSetAndPeekAndInvalidateAuthToken() { 1164 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1165 String expected = "x"; 1166 am.setAuthToken(ACCOUNT, AUTH_TOKEN_TYPE, expected); 1167 1168 // Ask for the AuthToken 1169 String token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE); 1170 assertNotNull(token); 1171 assertEquals(expected, token); 1172 1173 am.invalidateAuthToken(ACCOUNT_TYPE, token); 1174 token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE); 1175 assertNull(token); 1176 } 1177 1178 /** 1179 * Test successful blockingGetAuthToken() with customTokens=false authenticator. 1180 */ 1181 public void testBlockingGetAuthToken_DefaultToken_Success() 1182 throws IOException, AuthenticatorException, OperationCanceledException { 1183 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null); 1184 1185 String token = am.blockingGetAuthToken(ACCOUNT, 1186 AUTH_TOKEN_TYPE, 1187 false /* no failure notification */); 1188 1189 // Ask for the AuthToken 1190 assertNotNull(token); 1191 assertEquals(mockAuthenticator.getLastTokenServed(), token); 1192 } 1193 1194 private static class BlockingGetAuthTokenFetcher implements TokenFetcher { 1195 private final Account mAccount; 1196 1197 BlockingGetAuthTokenFetcher(Account account) { 1198 mAccount = account; 1199 } 1200 1201 @Override 1202 public Bundle fetch(String tokenType) 1203 throws OperationCanceledException, AuthenticatorException, IOException { 1204 String token = am.blockingGetAuthToken( 1205 getAccount(), 1206 tokenType, 1207 false /* no failure notification */); 1208 Bundle result = new Bundle(); 1209 result.putString(AccountManager.KEY_AUTHTOKEN, token); 1210 result.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); 1211 result.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type); 1212 return result; 1213 } 1214 @Override 1215 public Account getAccount() { 1216 return CUSTOM_TOKEN_ACCOUNT; 1217 } 1218 } 1219 1220 /** 1221 * Test successful blockingGetAuthToken() with customTokens=true authenticator. 1222 */ 1223 public void testBlockingGetAuthToken_CustomToken_NoCaching_Success() 1224 throws IOException, AuthenticatorException, OperationCanceledException { 1225 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1226 TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT); 1227 validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE); 1228 } 1229 1230 /** 1231 * Test successful blockingGetAuthToken() with customTokens=true authenticator. 1232 */ 1233 public void testBlockingGetAuthToken_CustomToken_ExpiringCache_Success() 1234 throws IOException, AuthenticatorException, OperationCanceledException { 1235 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1236 TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT); 1237 validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE); 1238 } 1239 1240 /** 1241 * Test successful getAuthToken() using a future with customTokens=false authenticator. 1242 */ 1243 public void testDeprecatedGetAuthTokenWithFuture_NoOptions_DefaultToken_Success() 1244 throws IOException, AuthenticatorException, OperationCanceledException { 1245 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1246 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1247 AUTH_TOKEN_TYPE, 1248 false /* no failure notification */, 1249 null /* no callback */, 1250 null /* no handler */ 1251 ); 1252 1253 Bundle resultBundle = futureBundle.getResult(); 1254 1255 assertTrue(futureBundle.isDone()); 1256 assertNotNull(resultBundle); 1257 1258 // Assert returned result 1259 validateAccountAndAuthTokenResult(resultBundle); 1260 } 1261 1262 /** 1263 * Test successful getAuthToken() using a future with customTokens=false without 1264 * expiring tokens. 1265 */ 1266 public void testDeprecatedGetAuthTokenWithFuture_NoOptions_CustomToken_Success() 1267 throws IOException, AuthenticatorException, OperationCanceledException { 1268 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1269 // validateSuccessfulTokenFetchingLifecycle(AccountManager am, TokenFetcher fetcher, String tokenType) 1270 TokenFetcher f = new TokenFetcher() { 1271 @Override 1272 public Bundle fetch(String tokenType) 1273 throws OperationCanceledException, AuthenticatorException, IOException { 1274 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken( 1275 getAccount(), 1276 tokenType, 1277 false /* no failure notification */, 1278 null /* no callback */, 1279 null /* no handler */ 1280 ); 1281 Bundle actual = futureBundle.getResult(); 1282 assertTrue(futureBundle.isDone()); 1283 assertNotNull(actual); 1284 return actual; 1285 } 1286 1287 @Override 1288 public Account getAccount() { 1289 return CUSTOM_TOKEN_ACCOUNT; 1290 } 1291 }; 1292 validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE); 1293 validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE); 1294 } 1295 1296 /** 1297 * Test successful getAuthToken() using a future with customTokens=false without 1298 * expiring tokens. 1299 */ 1300 public void testGetAuthTokenWithFuture_Options_DefaultToken_Success() 1301 throws IOException, AuthenticatorException, OperationCanceledException { 1302 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1303 1304 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1305 AUTH_TOKEN_TYPE, 1306 OPTIONS_BUNDLE, 1307 mActivity, 1308 null /* no callback */, 1309 null /* no handler */ 1310 ); 1311 1312 Bundle resultBundle = futureBundle.getResult(); 1313 1314 assertTrue(futureBundle.isDone()); 1315 assertNotNull(resultBundle); 1316 1317 // Assert returned result 1318 validateAccountAndAuthTokenResult(resultBundle); 1319 1320 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1321 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1322 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1323 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsGetAuthToken); 1324 validateSystemOptions(mockAuthenticator.mOptionsGetAuthToken); 1325 } 1326 1327 /** 1328 * Test successful getAuthToken() using a future with customTokens=false without 1329 * expiring tokens. 1330 */ 1331 public void testGetAuthTokenWithFuture_Options_CustomToken_Success() 1332 throws IOException, AuthenticatorException, OperationCanceledException { 1333 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1334 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1335 @Override 1336 public Bundle fetch(String tokenType) 1337 throws OperationCanceledException, AuthenticatorException, IOException { 1338 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken( 1339 getAccount(), 1340 tokenType, 1341 OPTIONS_BUNDLE, 1342 false /* no failure notification */, 1343 null /* no callback */, 1344 null /* no handler */ 1345 ); 1346 Bundle actual = futureBundle.getResult(); 1347 assertTrue(futureBundle.isDone()); 1348 assertNotNull(actual); 1349 return actual; 1350 } 1351 1352 @Override 1353 public Account getAccount() { 1354 return CUSTOM_TOKEN_ACCOUNT; 1355 } 1356 }; 1357 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1358 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1359 } 1360 1361 1362 /** 1363 * Test successful getAuthToken() using a future with customTokens=false without 1364 * expiring tokens. 1365 */ 1366 public void testGetAuthTokenWithCallback_Options_Handler_DefaultToken_Success() 1367 throws IOException, AuthenticatorException, OperationCanceledException { 1368 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null); 1369 final HandlerThread handlerThread = new HandlerThread("accounts.test"); 1370 handlerThread.start(); 1371 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1372 @Override 1373 public Bundle fetch(String tokenType) 1374 throws OperationCanceledException, AuthenticatorException, IOException { 1375 final AtomicReference<Bundle> actualRef = new AtomicReference<>(); 1376 final CountDownLatch latch = new CountDownLatch(1); 1377 1378 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1379 @Override 1380 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1381 Bundle resultBundle = null; 1382 try { 1383 resultBundle = bundleFuture.getResult(); 1384 actualRef.set(resultBundle); 1385 } catch (OperationCanceledException e) { 1386 fail("should not throw an OperationCanceledException"); 1387 } catch (IOException e) { 1388 fail("should not throw an IOException"); 1389 } catch (AuthenticatorException e) { 1390 fail("should not throw an AuthenticatorException"); 1391 } finally { 1392 latch.countDown(); 1393 } 1394 } 1395 }; 1396 1397 am.getAuthToken(getAccount(), 1398 tokenType, 1399 OPTIONS_BUNDLE, 1400 false /* no failure notification */, 1401 callback, 1402 new Handler(handlerThread.getLooper())); 1403 1404 // Wait with timeout for the callback to do its work 1405 try { 1406 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1407 } catch (InterruptedException e) { 1408 fail("should not throw an InterruptedException"); 1409 } 1410 return actualRef.get(); 1411 } 1412 1413 @Override 1414 public Account getAccount() { 1415 return ACCOUNT; 1416 } 1417 }; 1418 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1419 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1420 } 1421 1422 /** 1423 * Test successful getAuthToken() using a future with customTokens=false without 1424 * expiring tokens. 1425 */ 1426 public void testGetAuthTokenWithCallback_Options_Handler_CustomToken_Success() 1427 throws IOException, AuthenticatorException, OperationCanceledException { 1428 addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null); 1429 final HandlerThread handlerThread = new HandlerThread("accounts.test"); 1430 handlerThread.start(); 1431 TokenFetcher fetcherWithOptions = new TokenFetcher() { 1432 @Override 1433 public Bundle fetch(String tokenType) 1434 throws OperationCanceledException, AuthenticatorException, IOException { 1435 final AtomicReference<Bundle> actualRef = new AtomicReference<>(); 1436 final CountDownLatch latch = new CountDownLatch(1); 1437 1438 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1439 @Override 1440 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1441 Bundle resultBundle = null; 1442 try { 1443 resultBundle = bundleFuture.getResult(); 1444 actualRef.set(resultBundle); 1445 } catch (OperationCanceledException e) { 1446 fail("should not throw an OperationCanceledException"); 1447 } catch (IOException e) { 1448 fail("should not throw an IOException"); 1449 } catch (AuthenticatorException e) { 1450 fail("should not throw an AuthenticatorException"); 1451 } finally { 1452 latch.countDown(); 1453 } 1454 } 1455 }; 1456 1457 am.getAuthToken(getAccount(), 1458 tokenType, 1459 OPTIONS_BUNDLE, 1460 false /* no failure notification */, 1461 callback, 1462 new Handler(handlerThread.getLooper())); 1463 1464 // Wait with timeout for the callback to do its work 1465 try { 1466 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1467 } catch (InterruptedException e) { 1468 fail("should not throw an InterruptedException"); 1469 } 1470 return actualRef.get(); 1471 } 1472 1473 @Override 1474 public Account getAccount() { 1475 return CUSTOM_TOKEN_ACCOUNT; 1476 } 1477 }; 1478 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE); 1479 validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE); 1480 } 1481 1482 /** 1483 * Test getAuthToken() with callback and handler 1484 */ 1485 public void testGetAuthTokenWithCallbackAndHandler() throws IOException, AuthenticatorException, 1486 OperationCanceledException { 1487 1488 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1489 1490 testGetAuthTokenWithCallbackAndHandler(null /* handler */); 1491 testGetAuthTokenWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1492 } 1493 1494 private void testGetAuthTokenWithCallbackAndHandler(Handler handler) throws IOException, 1495 AuthenticatorException, OperationCanceledException { 1496 1497 final CountDownLatch latch = new CountDownLatch(1); 1498 1499 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1500 @Override 1501 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1502 1503 Bundle resultBundle = null; 1504 try { 1505 resultBundle = bundleFuture.getResult(); 1506 1507 // Assert returned result 1508 validateAccountAndAuthTokenResult(resultBundle); 1509 1510 } catch (OperationCanceledException e) { 1511 fail("should not throw an OperationCanceledException"); 1512 } catch (IOException e) { 1513 fail("should not throw an IOException"); 1514 } catch (AuthenticatorException e) { 1515 fail("should not throw an AuthenticatorException"); 1516 } 1517 finally { 1518 latch.countDown(); 1519 } 1520 } 1521 }; 1522 1523 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1524 AUTH_TOKEN_TYPE, 1525 false /* no failure notification */, 1526 callback, 1527 handler 1528 ); 1529 1530 Bundle resultBundle = futureBundle.getResult(); 1531 1532 assertTrue(futureBundle.isDone()); 1533 assertNotNull(resultBundle); 1534 1535 // Wait with timeout for the callback to do its work 1536 try { 1537 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1538 } catch (InterruptedException e) { 1539 fail("should not throw an InterruptedException"); 1540 } 1541 } 1542 1543 /** 1544 * test getAuthToken() with options and callback and handler 1545 */ 1546 public void testGetAuthTokenWithOptionsAndCallback() throws IOException, 1547 AuthenticatorException, OperationCanceledException { 1548 1549 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1550 1551 testGetAuthTokenWithOptionsAndCallbackAndHandler(null /* handler */); 1552 testGetAuthTokenWithOptionsAndCallbackAndHandler(new Handler(Looper.getMainLooper())); 1553 } 1554 1555 private void testGetAuthTokenWithOptionsAndCallbackAndHandler(Handler handler) throws 1556 IOException, AuthenticatorException, OperationCanceledException { 1557 1558 final CountDownLatch latch = new CountDownLatch(1); 1559 1560 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1561 @Override 1562 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1563 1564 Bundle resultBundle = null; 1565 try { 1566 resultBundle = bundleFuture.getResult(); 1567 // Assert returned result 1568 validateAccountAndAuthTokenResult(resultBundle); 1569 } catch (OperationCanceledException e) { 1570 fail("should not throw an OperationCanceledException"); 1571 } catch (IOException e) { 1572 fail("should not throw an IOException"); 1573 } catch (AuthenticatorException e) { 1574 fail("should not throw an AuthenticatorException"); 1575 } 1576 finally { 1577 latch.countDown(); 1578 } 1579 } 1580 }; 1581 1582 AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT, 1583 AUTH_TOKEN_TYPE, 1584 OPTIONS_BUNDLE, 1585 mActivity, 1586 callback, 1587 handler 1588 ); 1589 1590 Bundle resultBundle = futureBundle.getResult(); 1591 1592 assertTrue(futureBundle.isDone()); 1593 assertNotNull(resultBundle); 1594 1595 // Wait with timeout for the callback to do its work 1596 try { 1597 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1598 } catch (InterruptedException e) { 1599 fail("should not throw an InterruptedException"); 1600 } 1601 } 1602 1603 /** 1604 * Test getAuthTokenByFeatures() 1605 */ 1606 public void testGetAuthTokenByFeatures() throws IOException, AuthenticatorException, 1607 OperationCanceledException { 1608 1609 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1610 1611 Bundle resultBundle = getAuthTokenByFeature( 1612 new String[] { NON_EXISTING_FEATURE }, 1613 null /* activity */ 1614 ); 1615 1616 // Assert returned result 1617 validateNullResult(resultBundle); 1618 1619 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1620 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1621 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1622 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1623 1624 mockAuthenticator.clearData(); 1625 1626 // Now test with existing features and an activity 1627 resultBundle = getAuthTokenByFeature( 1628 new String[] { NON_EXISTING_FEATURE }, 1629 mActivity 1630 ); 1631 1632 // Assert returned result 1633 validateAccountAndAuthTokenResult(resultBundle); 1634 1635 validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount); 1636 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1637 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1638 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1639 1640 mockAuthenticator.clearData(); 1641 1642 // Now test with existing features and no activity 1643 resultBundle = getAuthTokenByFeature( 1644 REQUIRED_FEATURES, 1645 null /* activity */ 1646 ); 1647 1648 // Assert returned result 1649 validateAccountAndAuthTokenResult(resultBundle); 1650 1651 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1652 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1653 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1654 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1655 1656 mockAuthenticator.clearData(); 1657 1658 // Now test with existing features and an activity 1659 resultBundle = getAuthTokenByFeature( 1660 REQUIRED_FEATURES, 1661 mActivity 1662 ); 1663 1664 // Assert returned result 1665 validateAccountAndAuthTokenResult(resultBundle); 1666 1667 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 1668 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 1669 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 1670 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 1671 } 1672 1673 /** 1674 * Test confirmCredentials() 1675 */ 1676 @Presubmit 1677 public void testConfirmCredentials() throws IOException, AuthenticatorException, 1678 OperationCanceledException { 1679 1680 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1681 1682 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT, 1683 OPTIONS_BUNDLE, 1684 mActivity, 1685 null /* callback*/, 1686 null /* handler */); 1687 1688 futureBundle.getResult(); 1689 1690 // Assert returned result 1691 validateCredentials(); 1692 } 1693 1694 /** 1695 * Tests the setting of lastAuthenticatedTime on adding account 1696 */ 1697 // TODO: Either allow the system to see the activity from instant app, 1698 // Or separate the authenticator and test app to allow the instant app mode test. 1699 @AppModeFull 1700 public void testLastAuthenticatedTimeAfterAddAccount() throws IOException, 1701 AuthenticatorException, OperationCanceledException { 1702 assertTrue(addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD) > 0); 1703 } 1704 1705 /** 1706 * Test confirmCredentials() for account not on device. Just that no error 1707 * should be thrown. 1708 */ 1709 public void testConfirmCredentialsAccountNotOnDevice() throws IOException, 1710 AuthenticatorException, OperationCanceledException { 1711 1712 Account account = new Account("AccountNotOnThisDevice", ACCOUNT_TYPE); 1713 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(account, 1714 OPTIONS_BUNDLE, 1715 mActivity, 1716 null /* callback */, 1717 null /* handler */); 1718 1719 futureBundle.getResult(); 1720 } 1721 1722 /** 1723 * Tests the setting of lastAuthenticatedTime on confirmCredentials being 1724 * successful. 1725 */ 1726 // TODO: Either allow the system to see the activity from instant app, 1727 // Or separate the authenticator and test app to allow the instant app mode test. 1728 @AppModeFull 1729 public void testLastAuthenticatedTimeAfterConfirmCredentialsSuccess() throws IOException, 1730 AuthenticatorException, OperationCanceledException { 1731 1732 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1733 1734 // Now this confirm credentials call returns true, which in turn 1735 // should update the last authenticated timestamp. 1736 Bundle result = am.confirmCredentials(ACCOUNT, 1737 OPTIONS_BUNDLE, /* options */ 1738 null, /* activity */ 1739 null /* callback */, 1740 null /* handler */).getResult(); 1741 long confirmedCredTime = result.getLong( 1742 AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1); 1743 assertTrue(confirmedCredTime > accountAddTime); 1744 } 1745 1746 /** 1747 * Tests the setting of lastAuthenticatedTime on updateCredentials being 1748 * successful. 1749 */ 1750 // TODO: Either allow the system to see the activity from instant app, 1751 // Or separate the authenticator and test app to allow the instant app mode test. 1752 @AppModeFull 1753 public void testLastAuthenticatedTimeAfterUpdateCredentialsSuccess() throws IOException, 1754 AuthenticatorException, OperationCanceledException { 1755 1756 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1757 1758 am.updateCredentials(ACCOUNT, 1759 AUTH_TOKEN_TYPE, 1760 OPTIONS_BUNDLE, 1761 mActivity, 1762 null /* callback */, 1763 null /* handler */).getResult(); 1764 long updateCredTime = getLastAuthenticatedTime(ACCOUNT); 1765 assertTrue(updateCredTime > accountAddTime); 1766 } 1767 1768 /** 1769 * LastAuthenticatedTime on setPassword should not be disturbed. 1770 */ 1771 @AppModeFull(reason = "setPassword should be called by authenticator.") 1772 public void testLastAuthenticatedTimeAfterSetPassword() throws IOException, 1773 AuthenticatorException, OperationCanceledException { 1774 long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD); 1775 mockAuthenticator.callSetPassword(); 1776 long setPasswordTime = getLastAuthenticatedTime(ACCOUNT); 1777 assertTrue(setPasswordTime == accountAddTime); 1778 } 1779 1780 /** 1781 * Test confirmCredentials() with callback 1782 */ 1783 public void testConfirmCredentialsWithCallbackAndHandler() { 1784 1785 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1786 1787 testConfirmCredentialsWithCallbackAndHandler(null /* handler */); 1788 testConfirmCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1789 } 1790 1791 private void testConfirmCredentialsWithCallbackAndHandler(Handler handler) { 1792 final CountDownLatch latch = new CountDownLatch(1); 1793 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1794 @Override 1795 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1796 1797 Bundle resultBundle = null; 1798 try { 1799 resultBundle = bundleFuture.getResult(); 1800 1801 // Assert returned result 1802 validateCredentials(); 1803 1804 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 1805 } catch (OperationCanceledException e) { 1806 fail("should not throw an OperationCanceledException"); 1807 } catch (IOException e) { 1808 fail("should not throw an IOException"); 1809 } catch (AuthenticatorException e) { 1810 fail("should not throw an AuthenticatorException"); 1811 } 1812 finally { 1813 latch.countDown(); 1814 } 1815 } 1816 }; 1817 AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT, 1818 OPTIONS_BUNDLE, 1819 mActivity, 1820 callback, 1821 handler); 1822 // Wait with timeout for the callback to do its work 1823 try { 1824 latch.await(3 * LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1825 } catch (InterruptedException e) { 1826 fail("should not throw an InterruptedException"); 1827 } 1828 } 1829 1830 /** 1831 * Test updateCredentials() 1832 */ 1833 public void testUpdateCredentials() throws IOException, AuthenticatorException, 1834 OperationCanceledException { 1835 1836 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1837 1838 AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT, 1839 AUTH_TOKEN_TYPE, 1840 OPTIONS_BUNDLE, 1841 mActivity, 1842 null /* callback*/, 1843 null /* handler */); 1844 1845 Bundle result = futureBundle.getResult(); 1846 1847 validateAccountAndNoAuthTokenResult(result); 1848 1849 // Assert returned result 1850 validateCredentials(); 1851 } 1852 1853 /** 1854 * Test updateCredentials() with callback and handler 1855 */ 1856 public void testUpdateCredentialsWithCallbackAndHandler() throws IOException, 1857 AuthenticatorException, OperationCanceledException { 1858 1859 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 1860 1861 testUpdateCredentialsWithCallbackAndHandler(null /* handler */); 1862 testUpdateCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1863 } 1864 1865 private void testUpdateCredentialsWithCallbackAndHandler(Handler handler) throws IOException, 1866 AuthenticatorException, OperationCanceledException { 1867 1868 final CountDownLatch latch = new CountDownLatch(1); 1869 1870 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1871 @Override 1872 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1873 1874 Bundle resultBundle = null; 1875 try { 1876 resultBundle = bundleFuture.getResult(); 1877 1878 // Assert returned result 1879 validateCredentials(); 1880 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 1881 1882 } catch (OperationCanceledException e) { 1883 fail("should not throw an OperationCanceledException"); 1884 } catch (IOException e) { 1885 fail("should not throw an IOException"); 1886 } catch (AuthenticatorException e) { 1887 fail("should not throw an AuthenticatorException"); 1888 } 1889 finally { 1890 latch.countDown(); 1891 } 1892 } 1893 }; 1894 1895 AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT, 1896 AUTH_TOKEN_TYPE, 1897 OPTIONS_BUNDLE, 1898 mActivity, 1899 callback, 1900 handler); 1901 1902 futureBundle.getResult(); 1903 1904 // Wait with timeout for the callback to do its work 1905 try { 1906 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1907 } catch (InterruptedException e) { 1908 fail("should not throw an InterruptedException"); 1909 } 1910 } 1911 1912 /** 1913 * Test editProperties() 1914 */ 1915 public void testEditProperties() throws IOException, AuthenticatorException, 1916 OperationCanceledException { 1917 1918 AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE, 1919 mActivity, 1920 null /* callback */, 1921 null /* handler*/); 1922 1923 Bundle result = futureBundle.getResult(); 1924 1925 validateAccountAndNoAuthTokenResult(result); 1926 1927 // Assert returned result 1928 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 1929 } 1930 1931 /** 1932 * Test editProperties() with callback and handler 1933 */ 1934 public void testEditPropertiesWithCallbackAndHandler() { 1935 testEditPropertiesWithCallbackAndHandler(null /* handler */); 1936 testEditPropertiesWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 1937 } 1938 1939 private void testEditPropertiesWithCallbackAndHandler(Handler handler) { 1940 final CountDownLatch latch = new CountDownLatch(1); 1941 1942 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 1943 @Override 1944 public void run(AccountManagerFuture<Bundle> bundleFuture) { 1945 try { 1946 // Assert returned result 1947 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 1948 } 1949 finally { 1950 latch.countDown(); 1951 } 1952 } 1953 }; 1954 1955 AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE, 1956 mActivity, 1957 callback, 1958 handler); 1959 1960 // Wait with timeout for the callback to do its work 1961 try { 1962 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1963 } catch (InterruptedException e) { 1964 fail("should not throw an InterruptedException"); 1965 } 1966 } 1967 1968 /** 1969 * Test addOnAccountsUpdatedListener() with handler 1970 */ 1971 public void testAddOnAccountsUpdatedListenerWithHandler() throws IOException, 1972 AuthenticatorException, OperationCanceledException { 1973 1974 testAddOnAccountsUpdatedListenerWithHandler(null /* handler */, 1975 false /* updateImmediately */); 1976 1977 // Need to cleanup intermediate state 1978 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1979 AccountManager.KEY_BOOLEAN_RESULT)); 1980 1981 testAddOnAccountsUpdatedListenerWithHandler(null /* handler */, 1982 true /* updateImmediately */); 1983 1984 // Need to cleanup intermediate state 1985 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1986 AccountManager.KEY_BOOLEAN_RESULT)); 1987 1988 testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()), 1989 false /* updateImmediately */); 1990 1991 // Need to cleanup intermediate state 1992 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 1993 AccountManager.KEY_BOOLEAN_RESULT)); 1994 1995 testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()), 1996 true /* updateImmediately */); 1997 } 1998 1999 private void testAddOnAccountsUpdatedListenerWithHandler(Handler handler, 2000 boolean updateImmediately) { 2001 2002 final CountDownLatch latch = new CountDownLatch(1); 2003 OnAccountsUpdateListener listener = accounts -> latch.countDown(); 2004 2005 // Add a listener 2006 am.addOnAccountsUpdatedListener(listener, 2007 handler, 2008 updateImmediately); 2009 2010 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 2011 2012 // Wait with timeout for the callback to do its work 2013 try { 2014 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2015 } catch (InterruptedException e) { 2016 fail("should not throw an InterruptedException"); 2017 } 2018 2019 // Cleanup 2020 am.removeOnAccountsUpdatedListener(listener); 2021 } 2022 2023 /** 2024 * Test addOnAccountsUpdatedListener() with visibility 2025 */ 2026 public void testAddOnAccountsUpdatedListenerWithVisibility() throws IOException, 2027 AuthenticatorException, OperationCanceledException { 2028 2029 testAddOnAccountsUpdatedListenerWithVisibility(null /* handler */, 2030 false /* updateImmediately */, new String[] {ACCOUNT_TYPE, ACCOUNT_TYPE_ABSENT}); 2031 2032 // Need to cleanup intermediate state 2033 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 2034 AccountManager.KEY_BOOLEAN_RESULT)); 2035 2036 testAddOnAccountsUpdatedListenerWithVisibility(null /* handler */, 2037 true /* updateImmediately */, null /* types */); 2038 2039 // Need to cleanup intermediate state 2040 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 2041 AccountManager.KEY_BOOLEAN_RESULT)); 2042 2043 testAddOnAccountsUpdatedListenerWithVisibility(new Handler(Looper.getMainLooper()), 2044 false /* updateImmediately */, new String[] {ACCOUNT_TYPE}); 2045 2046 // Need to cleanup intermediate state 2047 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 2048 AccountManager.KEY_BOOLEAN_RESULT)); 2049 2050 testAddOnAccountsUpdatedListenerWithVisibility(new Handler(Looper.getMainLooper()), 2051 true /* updateImmediately */, new String[] {ACCOUNT_TYPE}); 2052 } 2053 2054 private void testAddOnAccountsUpdatedListenerWithVisibility(Handler handler, 2055 boolean updateImmediately, String[] accountTypes) { 2056 2057 final CountDownLatch latch = new CountDownLatch(1); 2058 OnAccountsUpdateListener listener = accounts -> latch.countDown(); 2059 2060 // Add a listener 2061 am.addOnAccountsUpdatedListener(listener, 2062 handler, 2063 updateImmediately, 2064 accountTypes); 2065 2066 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 2067 2068 // Wait with timeout for the callback to do its work 2069 try { 2070 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2071 } catch (InterruptedException e) { 2072 fail("should not throw an InterruptedException"); 2073 } 2074 2075 // Cleanup 2076 am.removeOnAccountsUpdatedListener(listener); 2077 } 2078 2079 /** 2080 * Test removeOnAccountsUpdatedListener() with handler 2081 */ 2082 public void testRemoveOnAccountsUpdatedListener() throws IOException, AuthenticatorException, 2083 OperationCanceledException { 2084 2085 testRemoveOnAccountsUpdatedListenerWithHandler(null /* handler */); 2086 2087 // Need to cleanup intermediate state 2088 assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean( 2089 AccountManager.KEY_BOOLEAN_RESULT)); 2090 2091 testRemoveOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper())); 2092 } 2093 2094 private void testRemoveOnAccountsUpdatedListenerWithHandler(Handler handler) { 2095 2096 final CountDownLatch latch = new CountDownLatch(1); 2097 OnAccountsUpdateListener listener = accounts -> fail("should not be called"); 2098 2099 // First add a listener 2100 am.addOnAccountsUpdatedListener(listener, 2101 handler, 2102 false /* updateImmediately */); 2103 2104 // Then remove the listener 2105 am.removeOnAccountsUpdatedListener(listener); 2106 2107 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 2108 2109 // Wait with timeout for the callback to do its work 2110 try { 2111 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2112 } catch (InterruptedException e) { 2113 fail("should not throw an InterruptedException"); 2114 } 2115 } 2116 2117 /** 2118 * Test hasFeature 2119 */ 2120 public void testHasFeature() 2121 throws IOException, AuthenticatorException, OperationCanceledException { 2122 2123 assertHasFeature(null /* handler */); 2124 assertHasFeature(new Handler(Looper.getMainLooper())); 2125 2126 assertHasFeatureWithCallback(null /* handler */); 2127 assertHasFeatureWithCallback(new Handler(Looper.getMainLooper())); 2128 } 2129 2130 private void assertHasFeature(Handler handler) 2131 throws IOException, AuthenticatorException, OperationCanceledException { 2132 Bundle resultBundle = addAccount(am, 2133 ACCOUNT_TYPE, 2134 AUTH_TOKEN_TYPE, 2135 REQUIRED_FEATURES, 2136 OPTIONS_BUNDLE, 2137 mActivity, 2138 null /* callback */, 2139 null /* handler */); 2140 2141 // Assert parameters has been passed correctly 2142 validateAccountAndAuthTokenType(); 2143 validateFeatures(); 2144 2145 AccountManagerFuture<Boolean> booleanFuture = am.hasFeatures(ACCOUNT, 2146 new String[]{FEATURE_1}, 2147 null /* callback */, 2148 handler); 2149 assertTrue(booleanFuture.getResult()); 2150 2151 booleanFuture = am.hasFeatures(ACCOUNT, 2152 new String[]{FEATURE_2}, 2153 null /* callback */, 2154 handler); 2155 assertTrue(booleanFuture.getResult()); 2156 2157 booleanFuture = am.hasFeatures(ACCOUNT, 2158 new String[]{FEATURE_1, FEATURE_2}, 2159 null /* callback */, 2160 handler); 2161 assertTrue(booleanFuture.getResult()); 2162 2163 booleanFuture = am.hasFeatures(ACCOUNT, 2164 new String[]{NON_EXISTING_FEATURE}, 2165 null /* callback */, 2166 handler); 2167 assertFalse(booleanFuture.getResult()); 2168 2169 booleanFuture = am.hasFeatures(ACCOUNT, 2170 new String[]{NON_EXISTING_FEATURE, FEATURE_1}, 2171 null /* callback */, 2172 handler); 2173 assertFalse(booleanFuture.getResult()); 2174 2175 booleanFuture = am.hasFeatures(ACCOUNT, 2176 new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2}, 2177 null /* callback */, 2178 handler); 2179 assertFalse(booleanFuture.getResult()); 2180 } 2181 2182 private AccountManagerCallback<Boolean> getAssertTrueCallback(final CountDownLatch latch) { 2183 return new AccountManagerCallback<Boolean>() { 2184 @Override 2185 public void run(AccountManagerFuture<Boolean> booleanFuture) { 2186 try { 2187 // Assert returned result should be TRUE 2188 assertTrue(booleanFuture.getResult()); 2189 } catch (Exception e) { 2190 fail("Exception: " + e); 2191 } finally { 2192 latch.countDown(); 2193 } 2194 } 2195 }; 2196 } 2197 2198 private AccountManagerCallback<Boolean> getAssertFalseCallback(final CountDownLatch latch) { 2199 return new AccountManagerCallback<Boolean>() { 2200 @Override 2201 public void run(AccountManagerFuture<Boolean> booleanFuture) { 2202 try { 2203 // Assert returned result should be FALSE 2204 assertFalse(booleanFuture.getResult()); 2205 } catch (Exception e) { 2206 fail("Exception: " + e); 2207 } finally { 2208 latch.countDown(); 2209 } 2210 } 2211 }; 2212 } 2213 2214 private void waitForLatch(final CountDownLatch latch) { 2215 // Wait with timeout for the callback to do its work 2216 try { 2217 latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2218 } catch (InterruptedException e) { 2219 fail("should not throw an InterruptedException"); 2220 } 2221 } 2222 2223 private void assertHasFeatureWithCallback(Handler handler) 2224 throws IOException, AuthenticatorException, OperationCanceledException { 2225 Bundle resultBundle = addAccount(am, 2226 ACCOUNT_TYPE, 2227 AUTH_TOKEN_TYPE, 2228 REQUIRED_FEATURES, 2229 OPTIONS_BUNDLE, 2230 mActivity, 2231 null /* callback */, 2232 null /* handler */); 2233 2234 // Assert parameters has been passed correctly 2235 validateAccountAndAuthTokenType(); 2236 validateFeatures(); 2237 2238 CountDownLatch latch = new CountDownLatch(1); 2239 am.hasFeatures(ACCOUNT, 2240 new String[]{FEATURE_1}, 2241 getAssertTrueCallback(latch), 2242 handler); 2243 waitForLatch(latch); 2244 2245 latch = new CountDownLatch(1); 2246 am.hasFeatures(ACCOUNT, 2247 new String[]{FEATURE_2}, 2248 getAssertTrueCallback(latch), 2249 handler); 2250 waitForLatch(latch); 2251 2252 latch = new CountDownLatch(1); 2253 am.hasFeatures(ACCOUNT, 2254 new String[]{FEATURE_1, FEATURE_2}, 2255 getAssertTrueCallback(latch), 2256 handler); 2257 waitForLatch(latch); 2258 2259 latch = new CountDownLatch(1); 2260 am.hasFeatures(ACCOUNT, 2261 new String[]{NON_EXISTING_FEATURE}, 2262 getAssertFalseCallback(latch), 2263 handler); 2264 waitForLatch(latch); 2265 2266 latch = new CountDownLatch(1); 2267 am.hasFeatures(ACCOUNT, 2268 new String[]{NON_EXISTING_FEATURE, FEATURE_1}, 2269 getAssertFalseCallback(latch), 2270 handler); 2271 waitForLatch(latch); 2272 2273 latch = new CountDownLatch(1); 2274 am.hasFeatures(ACCOUNT, 2275 new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2}, 2276 getAssertFalseCallback(latch), 2277 handler); 2278 waitForLatch(latch); 2279 } 2280 2281 private long getLastAuthenticatedTime(Account account) throws OperationCanceledException, 2282 AuthenticatorException, IOException { 2283 Bundle options = new Bundle(); 2284 options.putBoolean(MockAccountAuthenticator.KEY_RETURN_INTENT, true); 2285 // Not really confirming, but a way to get last authenticated timestamp 2286 Bundle result = am.confirmCredentials(account, 2287 options,// OPTIONS_BUNDLE, 2288 null, /* activity */ 2289 null /* callback */, 2290 null /* handler */).getResult(); 2291 return result.getLong( 2292 AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1); 2293 } 2294 2295 private long addAccountAndReturnAccountAddedTime(Account account, String password) 2296 throws OperationCanceledException, AuthenticatorException, IOException { 2297 addAccount(am, 2298 ACCOUNT_TYPE, 2299 AUTH_TOKEN_TYPE, 2300 REQUIRED_FEATURES, 2301 OPTIONS_BUNDLE, 2302 mActivity, 2303 null /* callback */, 2304 null /* handler */); 2305 return getLastAuthenticatedTime(account); 2306 } 2307 2308 /** 2309 * Tests that AccountManagerService is properly caching data. 2310 */ 2311 public void testGetsAreCached() { 2312 2313 // Add an account, 2314 assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT)); 2315 addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */); 2316 2317 // Then verify that we don't hit disk retrieving it, 2318 StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 2319 try { 2320 StrictMode.setThreadPolicy( 2321 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build()); 2322 // getAccounts() 2323 Account[] accounts = am.getAccounts(); 2324 assertNotNull(accounts); 2325 assertTrue(accounts.length > 0); 2326 2327 // getAccountsAndVisibilityForPackage(...) 2328 Map<Account, Integer> accountsAndVisibility = 2329 am.getAccountsAndVisibilityForPackage(PACKAGE_NAME_PRIVILEGED, ACCOUNT_TYPE); 2330 assertNotNull(accountsAndVisibility); 2331 assertTrue(accountsAndVisibility.size() > 0); 2332 2333 // getAccountsByType(...) 2334 Account[] accountsByType = am.getAccountsByType(ACCOUNT_TYPE); 2335 assertNotNull(accountsByType); 2336 assertTrue(accountsByType.length > 0); 2337 2338 // getAccountsByTypeForPackage(...) 2339 Account[] accountsByTypeForPackage = 2340 am.getAccountsByTypeForPackage(ACCOUNT_TYPE, PACKAGE_NAME_PRIVILEGED); 2341 assertNotNull(accountsByTypeForPackage); 2342 assertTrue(accountsByTypeForPackage.length > 0); 2343 2344 // getAccountsByTypeAndFeatures(...) 2345 am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, null /* features */, null, null); 2346 am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, REQUIRED_FEATURES, null, null); 2347 2348 } finally { 2349 StrictMode.setThreadPolicy(oldPolicy); 2350 } 2351 } 2352 2353 /** 2354 * Tests a basic startAddAccountSession() which returns a bundle containing 2355 * encrypted session bundle, account password and status token. 2356 */ 2357 public void testStartAddAccountSession() 2358 throws IOException, AuthenticatorException, OperationCanceledException { 2359 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 2360 + Fixtures.SUFFIX_NAME_FIXTURE; 2361 final Bundle options = createOptionsWithAccountName(accountName); 2362 2363 Bundle resultBundle = startAddAccountSession( 2364 am, 2365 ACCOUNT_TYPE, 2366 AUTH_TOKEN_TYPE, 2367 REQUIRED_FEATURES, 2368 options, 2369 null /* activity */, 2370 null /* callback */, 2371 null /* handler */); 2372 2373 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2374 2375 // Assert returned result 2376 // Assert that auth token was stripped. 2377 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2378 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2379 } 2380 2381 /** 2382 * Tests startAddAccountSession() with null session bundle. Only account 2383 * password and status token should be included in the result as session 2384 * bundle is not inspected. 2385 */ 2386 public void testStartAddAccountSessionWithNullSessionBundle() 2387 throws IOException, AuthenticatorException, OperationCanceledException { 2388 final Bundle options = new Bundle(); 2389 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 2390 + Fixtures.SUFFIX_NAME_FIXTURE; 2391 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 2392 options.putAll(OPTIONS_BUNDLE); 2393 2394 Bundle resultBundle = startAddAccountSession( 2395 am, 2396 ACCOUNT_TYPE, 2397 AUTH_TOKEN_TYPE, 2398 REQUIRED_FEATURES, 2399 options, 2400 null /* activity */, 2401 null /* callback */, 2402 null /* handler */); 2403 2404 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2405 2406 // Assert returned result 2407 // Assert that auth token was stripped. 2408 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2409 assertNull(resultBundle.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 2410 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 2411 assertEquals(ACCOUNT_STATUS_TOKEN, 2412 resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 2413 } 2414 2415 /** 2416 * Tests startAddAccountSession() with empty session bundle. An encrypted 2417 * session bundle, account password and status token should be included in 2418 * the result as session bundle is not inspected. 2419 */ 2420 public void testStartAddAccountSessionWithEmptySessionBundle() 2421 throws IOException, AuthenticatorException, OperationCanceledException { 2422 final Bundle options = new Bundle(); 2423 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 2424 + Fixtures.SUFFIX_NAME_FIXTURE; 2425 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 2426 options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, new Bundle()); 2427 options.putAll(OPTIONS_BUNDLE); 2428 2429 Bundle resultBundle = startAddAccountSession( 2430 am, 2431 ACCOUNT_TYPE, 2432 AUTH_TOKEN_TYPE, 2433 REQUIRED_FEATURES, 2434 options, 2435 null /* activity */, 2436 null /* callback */, 2437 null /* handler */); 2438 2439 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2440 2441 // Assert returned result 2442 // Assert that auth token was stripped. 2443 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2444 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2445 } 2446 2447 /** 2448 * Tests startAddAccountSession with authenticator activity started. When 2449 * Activity is provided, AccountManager would start the resolution Intent 2450 * and return the final result which contains an encrypted session bundle, 2451 * account password and status token. 2452 */ 2453 // TODO: Either allow the system to see the activity from instant app, 2454 // Or separate the authenticator and test app to allow the instant app mode test. 2455 @AppModeFull 2456 public void testStartAddAccountSessionIntervene() 2457 throws IOException, AuthenticatorException, OperationCanceledException { 2458 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 2459 + Fixtures.SUFFIX_NAME_FIXTURE; 2460 final Bundle options = createOptionsWithAccountName(accountName); 2461 2462 Bundle resultBundle = startAddAccountSession( 2463 am, 2464 ACCOUNT_TYPE, 2465 AUTH_TOKEN_TYPE, 2466 REQUIRED_FEATURES, 2467 options, 2468 mActivity, 2469 null /* callback */, 2470 null /* handler */); 2471 2472 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2473 2474 // Assert returned result 2475 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 2476 // Assert that auth token was stripped. 2477 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2478 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2479 } 2480 2481 /** 2482 * Tests startAddAccountSession with KEY_INTENT returned but not started 2483 * automatically. When no Activity is provided and authenticator requires 2484 * additional data from user, KEY_INTENT will be returned by AccountManager. 2485 */ 2486 // TODO: Either allow the system to see the activity from instant app, 2487 // Or separate the authenticator and test app to allow the instant app mode test. 2488 @AppModeFull 2489 public void testStartAddAccountSessionWithReturnIntent() 2490 throws IOException, AuthenticatorException, OperationCanceledException { 2491 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 2492 + Fixtures.SUFFIX_NAME_FIXTURE; 2493 final Bundle options = createOptionsWithAccountName(accountName); 2494 2495 Bundle resultBundle = startAddAccountSession( 2496 am, 2497 ACCOUNT_TYPE, 2498 AUTH_TOKEN_TYPE, 2499 REQUIRED_FEATURES, 2500 options, 2501 null /* activity */, 2502 null /* callback */, 2503 null /* handler */); 2504 2505 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2506 2507 // Assert returned result 2508 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 2509 // Assert that KEY_INTENT is returned. 2510 assertNotNull(returnIntent); 2511 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 2512 // Assert that no other data is returned. 2513 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 2514 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 2515 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 2516 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2517 } 2518 2519 /** 2520 * Tests startAddAccountSession error case. AuthenticatorException is 2521 * expected when authenticator return 2522 * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 2523 */ 2524 public void testStartAddAccountSessionError() throws IOException, OperationCanceledException { 2525 final String accountName = Fixtures.PREFIX_NAME_ERROR + "@" 2526 + Fixtures.SUFFIX_NAME_FIXTURE; 2527 final Bundle options = createOptionsWithAccountNameAndError(accountName); 2528 2529 try { 2530 startAddAccountSession( 2531 am, 2532 ACCOUNT_TYPE, 2533 AUTH_TOKEN_TYPE, 2534 REQUIRED_FEATURES, 2535 options, 2536 null /* activity */, 2537 null /* callback */, 2538 null /* handler */); 2539 fail("startAddAccountSession should throw AuthenticatorException in error case."); 2540 } catch (AuthenticatorException e) { 2541 } 2542 } 2543 2544 /** 2545 * Tests startAddAccountSession() with callback and handler. An encrypted 2546 * session bundle, account password and status token should be included in 2547 * the result. Callback should be triggered with the result regardless of a 2548 * handler is provided or not. 2549 */ 2550 public void testStartAddAccountSessionWithCallbackAndHandler() 2551 throws IOException, AuthenticatorException, OperationCanceledException { 2552 testStartAddAccountSessionWithCallbackAndHandler(null /* handler */); 2553 testStartAddAccountSessionWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 2554 } 2555 2556 /** 2557 * Tests startAddAccountSession() with callback and handler and activity 2558 * started. When Activity is provided, AccountManager would start the 2559 * resolution Intent and return the final result which contains an encrypted 2560 * session bundle, account password and status token. Callback should be 2561 * triggered with the result regardless of a handled is provided or not. 2562 */ 2563 // TODO: Either allow the system to see the activity from instant app, 2564 // Or separate the authenticator and test app to allow the instant app mode test. 2565 @AppModeFull 2566 public void testStartAddAccountSessionWithCallbackAndHandlerWithIntervene() 2567 throws IOException, AuthenticatorException, OperationCanceledException { 2568 testStartAddAccountSessionWithCallbackAndHandlerWithIntervene(null /* handler */); 2569 testStartAddAccountSessionWithCallbackAndHandlerWithIntervene( 2570 new Handler(Looper.getMainLooper())); 2571 } 2572 2573 /** 2574 * Tests startAddAccountSession() with callback and handler with KEY_INTENT 2575 * returned. When no Activity is provided and authenticator requires 2576 * additional data from user, KEY_INTENT will be returned by AccountManager 2577 * in callback regardless of a handler is provided or not. 2578 */ 2579 // TODO: Either allow the system to see the activity from instant app, 2580 // Or separate the authenticator and test app to allow the instant app mode test. 2581 @AppModeFull 2582 public void testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent() 2583 throws IOException, AuthenticatorException, OperationCanceledException { 2584 testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent(null /* handler */); 2585 testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent( 2586 new Handler(Looper.getMainLooper())); 2587 } 2588 2589 /** 2590 * Tests startAddAccountSession() error case with callback and handler. 2591 * AuthenticatorException is expected when authenticator return 2592 * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 2593 */ 2594 public void testStartAddAccountSessionErrorWithCallbackAndHandler() 2595 throws IOException, OperationCanceledException { 2596 testStartAddAccountSessionErrorWithCallbackAndHandler(null /* handler */); 2597 testStartAddAccountSessionErrorWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 2598 } 2599 2600 private void testStartAddAccountSessionWithCallbackAndHandler(Handler handler) 2601 throws IOException, AuthenticatorException, OperationCanceledException { 2602 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 2603 + Fixtures.SUFFIX_NAME_FIXTURE; 2604 final Bundle options = createOptionsWithAccountName(accountName); 2605 2606 // Wait with timeout for the callback to do its work 2607 final CountDownLatch latch = new CountDownLatch(1); 2608 2609 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 2610 @Override 2611 public void run(AccountManagerFuture<Bundle> bundleFuture) { 2612 Bundle resultBundle = getResultExpectNoException(bundleFuture); 2613 2614 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2615 2616 // Assert returned result 2617 // Assert that auth token was stripped. 2618 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2619 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2620 2621 latch.countDown(); 2622 } 2623 }; 2624 2625 startAddAccountSession( 2626 am, 2627 ACCOUNT_TYPE, 2628 AUTH_TOKEN_TYPE, 2629 REQUIRED_FEATURES, 2630 options, 2631 mActivity, 2632 callback, 2633 handler); 2634 waitForLatch(latch); 2635 } 2636 2637 private void testStartAddAccountSessionWithCallbackAndHandlerWithIntervene(Handler handler) 2638 throws IOException, AuthenticatorException, OperationCanceledException { 2639 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 2640 + Fixtures.SUFFIX_NAME_FIXTURE; 2641 final Bundle options = createOptionsWithAccountName(accountName); 2642 2643 // Wait with timeout for the callback to do its work 2644 final CountDownLatch latch = new CountDownLatch(1); 2645 2646 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 2647 @Override 2648 public void run(AccountManagerFuture<Bundle> bundleFuture) { 2649 Bundle resultBundle = getResultExpectNoException(bundleFuture); 2650 2651 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2652 2653 // Assert returned result 2654 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 2655 // Assert that auth token was stripped. 2656 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2657 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2658 2659 latch.countDown(); 2660 } 2661 }; 2662 2663 startAddAccountSession( 2664 am, 2665 ACCOUNT_TYPE, 2666 AUTH_TOKEN_TYPE, 2667 REQUIRED_FEATURES, 2668 options, 2669 mActivity, 2670 callback, 2671 handler); 2672 waitForLatch(latch); 2673 } 2674 2675 private void testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent(Handler handler) 2676 throws IOException, AuthenticatorException, OperationCanceledException { 2677 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 2678 + Fixtures.SUFFIX_NAME_FIXTURE; 2679 final Bundle options = createOptionsWithAccountName(accountName); 2680 2681 // Wait with timeout for the callback to do its work 2682 final CountDownLatch latch = new CountDownLatch(1); 2683 2684 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 2685 @Override 2686 public void run(AccountManagerFuture<Bundle> bundleFuture) { 2687 Bundle resultBundle = getResultExpectNoException(bundleFuture); 2688 2689 validateStartAddAccountSessionParametersAndOptions(accountName, options); 2690 2691 // Assert returned result 2692 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 2693 // Assert KEY_INTENT is returned. 2694 assertNotNull(returnIntent); 2695 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 2696 // Assert that no other data is returned. 2697 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 2698 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 2699 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 2700 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2701 2702 latch.countDown(); 2703 } 2704 }; 2705 2706 startAddAccountSession( 2707 am, 2708 ACCOUNT_TYPE, 2709 AUTH_TOKEN_TYPE, 2710 REQUIRED_FEATURES, 2711 options, 2712 null, // activity 2713 callback, 2714 handler); 2715 waitForLatch(latch); 2716 } 2717 2718 private void testStartAddAccountSessionErrorWithCallbackAndHandler(Handler handler) 2719 throws IOException, OperationCanceledException { 2720 final String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2721 final Bundle options = createOptionsWithAccountNameAndError(accountName); 2722 2723 // Wait with timeout for the callback to do its work 2724 final CountDownLatch latch = new CountDownLatch(1); 2725 2726 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 2727 @Override 2728 public void run(AccountManagerFuture<Bundle> bundleFuture) { 2729 try { 2730 bundleFuture.getResult(); 2731 fail("should have thrown an AuthenticatorException"); 2732 } catch (OperationCanceledException e) { 2733 fail("should not throw an OperationCanceledException"); 2734 } catch (IOException e) { 2735 fail("should not throw an IOException"); 2736 } catch (AuthenticatorException e) { 2737 latch.countDown(); 2738 } 2739 } 2740 }; 2741 2742 try { 2743 startAddAccountSession( 2744 am, 2745 ACCOUNT_TYPE, 2746 AUTH_TOKEN_TYPE, 2747 REQUIRED_FEATURES, 2748 options, 2749 mActivity, 2750 callback, 2751 handler); 2752 // AuthenticatorException should be thrown when authenticator 2753 // returns AccountManager.ERROR_CODE_INVALID_RESPONSE. 2754 fail("should have thrown an AuthenticatorException"); 2755 } catch (AuthenticatorException e1) { 2756 } 2757 2758 waitForLatch(latch); 2759 } 2760 2761 /** 2762 * Test a basic startUpdateCredentialsSession() which returns a bundle containing 2763 * encrypted session bundle, account password and status token. 2764 */ 2765 public void testStartUpdateCredentialsSession() 2766 throws IOException, AuthenticatorException, OperationCanceledException { 2767 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2768 Bundle options = createOptionsWithAccountName(accountName); 2769 2770 Bundle resultBundle = startUpdateCredentialsSession( 2771 am, 2772 ACCOUNT, 2773 AUTH_TOKEN_TYPE, 2774 options, 2775 null /* activity */, 2776 null /* callback */, 2777 null /* handler */); 2778 2779 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 2780 2781 // Assert returned result 2782 // Assert that auth token was stripped. 2783 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2784 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2785 } 2786 2787 /** 2788 * Tests startUpdateCredentialsSession() with null session bundle. Only account 2789 * password and status token should be included in the result as session 2790 * bundle is not inspected. 2791 */ 2792 public void testStartUpdateCredentialsSessionWithNullSessionBundle() 2793 throws IOException, AuthenticatorException, OperationCanceledException { 2794 Bundle options = new Bundle(); 2795 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2796 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 2797 options.putAll(OPTIONS_BUNDLE); 2798 2799 Bundle resultBundle = startUpdateCredentialsSession( 2800 am, 2801 ACCOUNT, 2802 AUTH_TOKEN_TYPE, 2803 options, 2804 null /* activity */, 2805 null /* callback */, 2806 null /* handler */); 2807 2808 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 2809 2810 // Assert returned result 2811 // Assert that auth token was stripped. 2812 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2813 assertNull(resultBundle.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 2814 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 2815 assertEquals(ACCOUNT_STATUS_TOKEN, 2816 resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 2817 } 2818 2819 /** 2820 * Tests startUpdateCredentialsSession() with empty session bundle. An encrypted 2821 * session bundle, account password and status token should be included in 2822 * the result as session bundle is not inspected. 2823 */ 2824 public void testStartUpdateCredentialsSessionWithEmptySessionBundle() 2825 throws IOException, AuthenticatorException, OperationCanceledException { 2826 Bundle options = new Bundle(); 2827 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2828 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 2829 options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, new Bundle()); 2830 options.putAll(OPTIONS_BUNDLE); 2831 2832 Bundle resultBundle = startUpdateCredentialsSession( 2833 am, 2834 ACCOUNT, 2835 AUTH_TOKEN_TYPE, 2836 options, 2837 null /* activity */, 2838 null /* callback */, 2839 null /* handler */); 2840 2841 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 2842 2843 // Assert returned result 2844 // Assert that auth token was stripped. 2845 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2846 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2847 } 2848 2849 /** 2850 * Tests startUpdateCredentialsSession with authenticator activity started. When 2851 * Activity is provided, AccountManager would start the resolution Intent 2852 * and return the final result which contains an encrypted session bundle, 2853 * account password and status token. 2854 */ 2855 // TODO: Either allow the system to see the activity from instant app, 2856 // Or separate the authenticator and test app to allow the instant app mode test. 2857 @AppModeFull 2858 public void testStartUpdateCredentialsSessionIntervene() 2859 throws IOException, AuthenticatorException, OperationCanceledException { 2860 String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2861 Bundle options = createOptionsWithAccountName(accountName); 2862 2863 Bundle resultBundle = startUpdateCredentialsSession( 2864 am, 2865 ACCOUNT, 2866 AUTH_TOKEN_TYPE, 2867 options, 2868 mActivity, 2869 null /* callback */, 2870 null /* handler */); 2871 2872 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 2873 2874 // Assert returned result 2875 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 2876 // Assert that auth token was stripped. 2877 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2878 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 2879 } 2880 2881 /** 2882 * Tests startUpdateCredentialsSession with KEY_INTENT returned but not 2883 * started automatically. When no Activity is provided and authenticator requires 2884 * additional data from user, KEY_INTENT will be returned by AccountManager. 2885 */ 2886 // TODO: Either allow the system to see the activity from instant app, 2887 // Or separate the authenticator and test app to allow the instant app mode test. 2888 @AppModeFull 2889 public void testStartUpdateCredentialsSessionWithReturnIntent() 2890 throws IOException, AuthenticatorException, OperationCanceledException { 2891 String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2892 Bundle options = createOptionsWithAccountName(accountName); 2893 2894 Bundle resultBundle = startUpdateCredentialsSession( 2895 am, 2896 ACCOUNT, 2897 AUTH_TOKEN_TYPE, 2898 options, 2899 null /* activity */, 2900 null /* callback */, 2901 null /* handler */); 2902 2903 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 2904 2905 // Assert returned result 2906 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 2907 // Assert that KEY_INTENT is returned. 2908 assertNotNull(returnIntent); 2909 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 2910 // Assert that no other data is returned. 2911 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 2912 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 2913 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 2914 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 2915 } 2916 2917 /** 2918 * Tests startUpdateCredentialsSession error case. AuthenticatorException is 2919 * expected when authenticator return 2920 * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 2921 */ 2922 public void testStartUpdateCredentialsSessionError() 2923 throws IOException, OperationCanceledException { 2924 String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 2925 Bundle options = createOptionsWithAccountNameAndError(accountName); 2926 2927 try { 2928 startUpdateCredentialsSession( 2929 am, 2930 ACCOUNT, 2931 AUTH_TOKEN_TYPE, 2932 options, 2933 null /* activity */, 2934 null /* callback */, 2935 null /* handler */); 2936 fail("startUpdateCredentialsSession should throw AuthenticatorException in error."); 2937 } catch (AuthenticatorException e) { 2938 } 2939 } 2940 2941 /** 2942 * Tests startUpdateCredentialsSession() with callback and handler. An encrypted 2943 * session bundle, account password and status token should be included in 2944 * the result. Callback should be triggered with the result regardless of a 2945 * handler is provided or not. 2946 */ 2947 public void testStartUpdateCredentialsSessionWithCallbackAndHandler() 2948 throws IOException, AuthenticatorException, OperationCanceledException { 2949 testStartUpdateCredentialsSessionWithCallbackAndHandler(null /* handler */); 2950 testStartUpdateCredentialsSessionWithCallbackAndHandler( 2951 new Handler(Looper.getMainLooper())); 2952 } 2953 2954 /** 2955 * Tests startUpdateCredentialsSession() with callback and handler and 2956 * activity started. When Activity is provided, AccountManager would start the 2957 * resolution Intent and return the final result which contains an encrypted 2958 * session bundle, account password and status token. Callback should be 2959 * triggered with the result regardless of a handler is provided or not. 2960 */ 2961 // TODO: Either allow the system to see the activity from instant app, 2962 // Or separate the authenticator and test app to allow the instant app mode test. 2963 @AppModeFull 2964 public void testStartUpdateCredentialsSessionWithCallbackAndHandlerWithIntervene() 2965 throws IOException, AuthenticatorException, OperationCanceledException { 2966 testStartUpdateCredentialsSessionWithCallbackAndHandlerWithIntervene(null /* handler */); 2967 testStartUpdateCredentialsSessionWithCallbackAndHandlerWithIntervene( 2968 new Handler(Looper.getMainLooper())); 2969 } 2970 2971 /** 2972 * Tests startUpdateCredentialsSession() with callback and handler with 2973 * KEY_INTENT returned. When no Activity is provided and authenticator requires 2974 * additional data from user, KEY_INTENT will be returned by AccountManager 2975 * in callback regardless of a handler is provided or not. 2976 */ 2977 // TODO: Either allow the system to see the activity from instant app, 2978 // Or separate the authenticator and test app to allow the instant app mode test. 2979 @AppModeFull 2980 public void testStartUpdateCredentialsSessionWithCallbackAndHandlerWithReturnIntent() 2981 throws IOException, AuthenticatorException, OperationCanceledException { 2982 testStartUpdateCredentialsSessionWithCallbackAndHandlerWithReturnIntent(null /* handler */); 2983 testStartUpdateCredentialsSessionWithCallbackAndHandlerWithReturnIntent( 2984 new Handler(Looper.getMainLooper())); 2985 } 2986 2987 /** 2988 * Tests startUpdateCredentialsSession() error case with callback and 2989 * handler. AuthenticatorException is expected when authenticator return 2990 * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 2991 */ 2992 public void testStartUpdateCredentialsSessionErrorWithCallbackAndHandler() 2993 throws IOException, OperationCanceledException { 2994 testStartUpdateCredentialsSessionErrorWithCallbackAndHandler(null /* handler */); 2995 testStartUpdateCredentialsSessionErrorWithCallbackAndHandler( 2996 new Handler(Looper.getMainLooper())); 2997 } 2998 2999 private void testStartUpdateCredentialsSessionWithCallbackAndHandler(Handler handler) 3000 throws IOException, AuthenticatorException, OperationCanceledException { 3001 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 3002 + Fixtures.SUFFIX_NAME_FIXTURE; 3003 final Bundle options = createOptionsWithAccountName(accountName); 3004 3005 final CountDownLatch latch = new CountDownLatch(1); 3006 3007 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3008 @Override 3009 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3010 Bundle resultBundle = getResultExpectNoException(bundleFuture); 3011 3012 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 3013 3014 // Assert returned result 3015 // Assert that auth token was stripped. 3016 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3017 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3018 3019 latch.countDown(); 3020 } 3021 }; 3022 3023 startUpdateCredentialsSession( 3024 am, 3025 ACCOUNT, 3026 AUTH_TOKEN_TYPE, 3027 options, 3028 mActivity, 3029 callback, 3030 handler); 3031 3032 waitForLatch(latch); 3033 } 3034 3035 private void testStartUpdateCredentialsSessionWithCallbackAndHandlerWithIntervene( 3036 Handler handler) 3037 throws IOException, AuthenticatorException, OperationCanceledException { 3038 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 3039 + Fixtures.SUFFIX_NAME_FIXTURE; 3040 final Bundle options = createOptionsWithAccountName(accountName); 3041 3042 final CountDownLatch latch = new CountDownLatch(1); 3043 3044 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3045 @Override 3046 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3047 Bundle resultBundle = getResultExpectNoException(bundleFuture); 3048 3049 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 3050 3051 // Assert returned result 3052 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3053 // Assert that auth token was stripped. 3054 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3055 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3056 3057 latch.countDown(); 3058 } 3059 }; 3060 3061 startUpdateCredentialsSession( 3062 am, 3063 ACCOUNT, 3064 AUTH_TOKEN_TYPE, 3065 options, 3066 mActivity, 3067 callback, 3068 handler); 3069 3070 waitForLatch(latch); 3071 } 3072 3073 private void testStartUpdateCredentialsSessionWithCallbackAndHandlerWithReturnIntent( 3074 Handler handler) 3075 throws IOException, AuthenticatorException, OperationCanceledException { 3076 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 3077 + Fixtures.SUFFIX_NAME_FIXTURE; 3078 final Bundle options = createOptionsWithAccountName(accountName); 3079 3080 final CountDownLatch latch = new CountDownLatch(1); 3081 3082 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3083 @Override 3084 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3085 Bundle resultBundle = getResultExpectNoException(bundleFuture); 3086 3087 validateStartUpdateCredentialsSessionParametersAndOptions(accountName, options); 3088 3089 // Assert returned result 3090 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 3091 // Assert KEY_INTENT is returned. 3092 assertNotNull(returnIntent); 3093 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 3094 // Assert that no other data is returned. 3095 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 3096 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 3097 assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); 3098 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3099 3100 latch.countDown(); 3101 } 3102 }; 3103 3104 startUpdateCredentialsSession( 3105 am, 3106 ACCOUNT, 3107 AUTH_TOKEN_TYPE, 3108 options, 3109 null, 3110 callback, 3111 handler); 3112 3113 waitForLatch(latch); 3114 } 3115 3116 private void testStartUpdateCredentialsSessionErrorWithCallbackAndHandler(Handler handler) 3117 throws IOException, OperationCanceledException { 3118 final String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3119 final Bundle options = createOptionsWithAccountNameAndError(accountName); 3120 3121 final CountDownLatch latch = new CountDownLatch(1); 3122 3123 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3124 @Override 3125 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3126 try { 3127 bundleFuture.getResult(); 3128 fail("should have thrown an AuthenticatorException"); 3129 } catch (OperationCanceledException e) { 3130 fail("should not throw an OperationCanceledException"); 3131 } catch (IOException e) { 3132 fail("should not throw an IOException"); 3133 } catch (AuthenticatorException e) { 3134 latch.countDown(); 3135 } 3136 } 3137 }; 3138 3139 try { 3140 startUpdateCredentialsSession( 3141 am, 3142 ACCOUNT, 3143 AUTH_TOKEN_TYPE, 3144 options, 3145 mActivity, 3146 callback, 3147 handler); 3148 // AuthenticatorException should be thrown when authenticator 3149 // returns AccountManager.ERROR_CODE_INVALID_RESPONSE. 3150 fail("should have thrown an AuthenticatorException"); 3151 } catch (AuthenticatorException e1) { 3152 } 3153 3154 waitForLatch(latch); 3155 } 3156 3157 private Bundle startUpdateCredentialsSession(AccountManager am, 3158 Account account, 3159 String authTokenType, 3160 Bundle options, 3161 Activity activity, 3162 AccountManagerCallback<Bundle> callback, 3163 Handler handler) 3164 throws IOException, AuthenticatorException, OperationCanceledException { 3165 3166 AccountManagerFuture<Bundle> futureBundle = am.startUpdateCredentialsSession( 3167 account, 3168 authTokenType, 3169 options, 3170 activity, 3171 callback, 3172 handler); 3173 3174 Bundle resultBundle = futureBundle.getResult(); 3175 assertTrue(futureBundle.isDone()); 3176 assertNotNull(resultBundle); 3177 3178 return resultBundle; 3179 } 3180 3181 private Bundle startAddAccountSession(AccountManager am, 3182 String accountType, 3183 String authTokenType, 3184 String[] requiredFeatures, 3185 Bundle options, 3186 Activity activity, 3187 AccountManagerCallback<Bundle> callback, 3188 Handler handler) 3189 throws IOException, AuthenticatorException, OperationCanceledException { 3190 3191 AccountManagerFuture<Bundle> futureBundle = am.startAddAccountSession( 3192 accountType, 3193 authTokenType, 3194 requiredFeatures, 3195 options, 3196 activity, 3197 callback, 3198 handler); 3199 3200 Bundle resultBundle = futureBundle.getResult(); 3201 assertTrue(futureBundle.isDone()); 3202 assertNotNull(resultBundle); 3203 3204 return resultBundle; 3205 } 3206 3207 private Bundle createOptionsWithAccountName(final String accountName) { 3208 SESSION_BUNDLE.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 3209 Bundle options = new Bundle(); 3210 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 3211 options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, SESSION_BUNDLE); 3212 options.putAll(OPTIONS_BUNDLE); 3213 return options; 3214 } 3215 3216 private Bundle createOptionsWithAccountNameAndError(final String accountName) { 3217 Bundle options = createOptionsWithAccountName(accountName); 3218 options.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE); 3219 options.putString(AccountManager.KEY_ERROR_MESSAGE, ERROR_MESSAGE); 3220 return options; 3221 } 3222 3223 3224 private void validateStartAddAccountSessionParametersAndOptions( 3225 String accountName, Bundle options) { 3226 // Assert parameters has been passed correctly 3227 validateAccountAndAuthTokenType(); 3228 validateFeatures(); 3229 3230 // Validate options 3231 validateOptions(options, mockAuthenticator.mOptionsStartAddAccountSession); 3232 assertNotNull(mockAuthenticator.mOptionsStartAddAccountSession); 3233 assertEquals(accountName, mockAuthenticator.mOptionsStartAddAccountSession 3234 .getString(Fixtures.KEY_ACCOUNT_NAME)); 3235 3236 validateSystemOptions(mockAuthenticator.mOptionsStartAddAccountSession); 3237 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 3238 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 3239 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 3240 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 3241 validateOptions(null, mockAuthenticator.mOptionsStartUpdateCredentialsSession); 3242 validateOptions(null, mockAuthenticator.mOptionsFinishSession); 3243 } 3244 3245 private void validateStartUpdateCredentialsSessionParametersAndOptions( 3246 String accountName, Bundle options) { 3247 // Assert parameters has been passed correctly 3248 assertEquals(AUTH_TOKEN_TYPE, mockAuthenticator.getAuthTokenType()); 3249 assertEquals(ACCOUNT, mockAuthenticator.mAccount); 3250 3251 // Validate options 3252 validateOptions(options, mockAuthenticator.mOptionsStartUpdateCredentialsSession); 3253 assertNotNull(mockAuthenticator.mOptionsStartUpdateCredentialsSession); 3254 assertEquals(accountName, mockAuthenticator.mOptionsStartUpdateCredentialsSession 3255 .getString(Fixtures.KEY_ACCOUNT_NAME)); 3256 3257 // Validate system options 3258 assertNotNull(mockAuthenticator.mOptionsStartUpdateCredentialsSession 3259 .getString(AccountManager.KEY_ANDROID_PACKAGE_NAME)); 3260 3261 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 3262 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 3263 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 3264 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 3265 validateOptions(null, mockAuthenticator.mOptionsStartAddAccountSession); 3266 validateOptions(null, mockAuthenticator.mOptionsFinishSession); 3267 } 3268 3269 private void validateIsCredentialsUpdateSuggestedParametersAndOptions(Account account) { 3270 assertEquals(account, mockAuthenticator.getAccount()); 3271 assertEquals(ACCOUNT_STATUS_TOKEN, mockAuthenticator.getStatusToken()); 3272 3273 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 3274 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 3275 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 3276 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 3277 validateOptions(null, mockAuthenticator.mOptionsStartUpdateCredentialsSession); 3278 validateOptions(null, mockAuthenticator.mOptionsStartAddAccountSession); 3279 } 3280 3281 private void validateSessionBundleAndPasswordAndStatusTokenResult(Bundle resultBundle) { 3282 Bundle sessionBundle = resultBundle.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3283 assertNotNull(sessionBundle); 3284 // Assert that session bundle is encrypted and hence data not visible. 3285 assertNull(sessionBundle.getString(SESSION_DATA_NAME_1)); 3286 // Assert password is not returned since cts test is not signed with system key 3287 assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD)); 3288 assertEquals(ACCOUNT_STATUS_TOKEN, 3289 resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN)); 3290 } 3291 3292 private Bundle getResultExpectNoException(AccountManagerFuture<Bundle> bundleFuture) { 3293 try { 3294 return bundleFuture.getResult(); 3295 } catch (OperationCanceledException e) { 3296 fail("should not throw an OperationCanceledException"); 3297 } catch (IOException e) { 3298 fail("should not throw an IOException"); 3299 } catch (AuthenticatorException e) { 3300 fail("should not throw an AuthenticatorException"); 3301 } 3302 return null; 3303 } 3304 3305 /** 3306 * Tests a basic finishSession() with session bundle created by 3307 * startAddAccountSession(...). A bundle containing account name and account 3308 * type is expected. 3309 */ 3310 public void testFinishSessionWithStartAddAccountSession() 3311 throws IOException, AuthenticatorException, OperationCanceledException { 3312 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3313 Bundle options = createOptionsWithAccountName(accountName); 3314 3315 // First get an encrypted session bundle from startAddAccountSession(...) 3316 Bundle resultBundle = startAddAccountSession( 3317 am, 3318 ACCOUNT_TYPE, 3319 AUTH_TOKEN_TYPE, 3320 REQUIRED_FEATURES, 3321 options, 3322 null /* activity */, 3323 null /* callback */, 3324 null /* handler */); 3325 3326 // Assert returned result 3327 // Assert that auth token was stripped. 3328 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3329 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3330 Bundle encryptedSessionBundle = resultBundle 3331 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3332 3333 resultBundle = finishSession( 3334 am, 3335 encryptedSessionBundle, 3336 null /* activity */, 3337 null /* callback */, 3338 null /* handler */); 3339 3340 // Assert parameters has been passed correctly 3341 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3342 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3343 3344 // Assert returned result containing account name, type but not auth token type. 3345 validateAccountAndNoAuthTokenResult(resultBundle); 3346 } 3347 3348 /** 3349 * Tests a basic finishSession() with session bundle created by 3350 * startUpdateCredentialsSession(...). A bundle containing account name and account 3351 * type is expected. 3352 */ 3353 public void testFinishSessionWithStartUpdateCredentialsSession() 3354 throws IOException, AuthenticatorException, OperationCanceledException { 3355 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3356 Bundle options = createOptionsWithAccountName(accountName); 3357 3358 // First get an encrypted session bundle from startUpdateCredentialsSession(...) 3359 Bundle resultBundle = startUpdateCredentialsSession( 3360 am, 3361 ACCOUNT, 3362 AUTH_TOKEN_TYPE, 3363 options, 3364 null /* activity */, 3365 null /* callback */, 3366 null /* handler */); 3367 3368 // Assert returned result 3369 // Assert that auth token was stripped. 3370 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3371 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3372 Bundle encryptedSessionBundle = resultBundle 3373 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3374 3375 resultBundle = finishSession( 3376 am, 3377 encryptedSessionBundle, 3378 null /* activity */, 3379 null /* callback */, 3380 null /* handler */); 3381 3382 // Assert parameters has been passed correctly 3383 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3384 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3385 3386 // Assert returned result containing account name, type but not auth token type. 3387 validateAccountAndNoAuthTokenResult(resultBundle); 3388 } 3389 3390 /** 3391 * Tests finishSession() with null session bundle. IllegalArgumentException 3392 * is expected as session bundle cannot be null. 3393 */ 3394 public void testFinishSessionWithNullSessionBundle() 3395 throws IOException, AuthenticatorException, OperationCanceledException { 3396 try { 3397 finishSession( 3398 am, 3399 null /* sessionBundle */, 3400 null /* activity */, 3401 null /* callback */, 3402 null /* handler */); 3403 fail("Should have thrown IllegalArgumentException when sessionBundle is null"); 3404 } catch (IllegalArgumentException e) { 3405 3406 } 3407 } 3408 3409 /** 3410 * Tests finishSession() with empty session bundle. IllegalArgumentException 3411 * is expected as session bundle would always contain something if it was 3412 * processed properly by AccountManagerService. 3413 */ 3414 public void testFinishSessionWithEmptySessionBundle() 3415 throws IOException, AuthenticatorException, OperationCanceledException { 3416 3417 try { 3418 finishSession(am, 3419 new Bundle(), 3420 null /* activity */, 3421 null /* callback */, 3422 null /* handler */); 3423 fail("Should have thrown IllegalArgumentException when sessionBundle is empty"); 3424 } catch (IllegalArgumentException e) { 3425 3426 } 3427 } 3428 3429 /** 3430 * Tests finishSession() with sessionBundle not encrypted by the right key. 3431 * AuthenticatorException is expected if AccountManagerService failed to 3432 * decrypt the session bundle because of wrong key or crypto data was 3433 * tampered. 3434 */ 3435 public void testFinishSessionWithDecryptionError() 3436 throws IOException, OperationCanceledException { 3437 byte[] mac = new byte[] { 3438 1, 1, 0, 0 3439 }; 3440 byte[] cipher = new byte[] { 3441 1, 0, 0, 1, 1 3442 }; 3443 Bundle sessionBundle = new Bundle(); 3444 sessionBundle.putByteArray(KEY_MAC, mac); 3445 sessionBundle.putByteArray(KEY_CIPHER, cipher); 3446 3447 try { 3448 finishSession(am, 3449 sessionBundle, 3450 null /* activity */, 3451 null /* callback */, 3452 null /* handler */); 3453 fail("Should have thrown AuthenticatorException when failed to decrypt sessionBundle"); 3454 } catch (AuthenticatorException e) { 3455 3456 } 3457 } 3458 3459 /** 3460 * Tests finishSession() with sessionBundle invalid contents. 3461 * AuthenticatorException is expected if AccountManagerService failed to 3462 * decrypt the session bundle because of wrong key or crypto data was 3463 * tampered. 3464 */ 3465 public void testFinishSessionWithInvalidEncryptedContent() 3466 throws IOException, OperationCanceledException { 3467 byte[] mac = new byte[] {}; 3468 Bundle sessionBundle = new Bundle(); 3469 sessionBundle.putByteArray(KEY_MAC, mac); 3470 3471 try { 3472 finishSession(am, 3473 sessionBundle, 3474 null /* activity */, 3475 null /* callback */, 3476 null /* handler */); 3477 fail("Should have thrown AuthenticatorException when failed to decrypt sessionBundle"); 3478 } catch (AuthenticatorException e) { 3479 } 3480 } 3481 3482 /** 3483 * Tests a finishSession() when account type is not added to session bundle 3484 * by startAddAccount(...) of authenticator. A bundle containing account 3485 * name and account type should still be returned as AccountManagerSerivce 3486 * will always add account type to the session bundle before encrypting it. 3487 */ 3488 public void testFinishSessionFromStartAddAccountWithoutAccountType() 3489 throws IOException, AuthenticatorException, OperationCanceledException { 3490 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3491 // Create a session bundle without account type for MockAccountAuthenticator to return 3492 SESSION_BUNDLE.remove(AccountManager.KEY_ACCOUNT_TYPE); 3493 Bundle options = createOptionsWithAccountName(accountName); 3494 3495 // First get an encrypted session bundle from startAddAccountSession(...) 3496 Bundle resultBundle = startAddAccountSession( 3497 am, 3498 ACCOUNT_TYPE, 3499 AUTH_TOKEN_TYPE, 3500 REQUIRED_FEATURES, 3501 options, 3502 null /* activity */, 3503 null /* callback */, 3504 null /* handler */); 3505 3506 // Assert returned result 3507 // Assert that auth token was stripped. 3508 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3509 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3510 Bundle encryptedSessionBundle = resultBundle 3511 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3512 3513 resultBundle = finishSession( 3514 am, 3515 encryptedSessionBundle, 3516 null /* activity */, 3517 null /* callback */, 3518 null /* handler */); 3519 3520 // Assert parameters has been passed correctly 3521 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3522 3523 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3524 3525 // Assert returned result containing account name, type but not auth token type. 3526 validateAccountAndNoAuthTokenResult(resultBundle); 3527 } 3528 3529 /** 3530 * Tests a finishSession() when account type is not added to session bundle 3531 * by startUpdateCredentialsSession(...) of authenticator. A bundle 3532 * containing account name and account type should still be returned as 3533 * AccountManagerSerivce will always add account type to the session bundle 3534 * before encrypting it. 3535 */ 3536 public void testFinishSessionFromStartUpdateCredentialsSessionWithoutAccountType() 3537 throws IOException, AuthenticatorException, OperationCanceledException { 3538 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3539 // Create a session bundle without account type for MockAccountAuthenticator to return 3540 SESSION_BUNDLE.remove(AccountManager.KEY_ACCOUNT_TYPE); 3541 Bundle options = createOptionsWithAccountName(accountName); 3542 3543 // First get an encrypted session bundle from startAddAccountSession(...) 3544 Bundle resultBundle = startUpdateCredentialsSession( 3545 am, 3546 ACCOUNT, 3547 AUTH_TOKEN_TYPE, 3548 options, 3549 null /* activity */, 3550 null /* callback */, 3551 null /* handler */); 3552 3553 // Assert returned result 3554 // Assert that auth token was stripped. 3555 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3556 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3557 Bundle encryptedSessionBundle = resultBundle 3558 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3559 3560 resultBundle = finishSession( 3561 am, 3562 encryptedSessionBundle, 3563 null /* activity */, 3564 null /* callback */, 3565 null /* handler */); 3566 3567 // Assert parameters has been passed correctly 3568 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3569 3570 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3571 3572 // Assert returned result containing account name, type but not auth token type. 3573 validateAccountAndNoAuthTokenResult(resultBundle); 3574 } 3575 3576 /** 3577 * Tests a finishSession() when a different account type is added to session bundle 3578 * by startAddAccount(...) of authenticator. A bundle containing account 3579 * name and the correct account type should be returned as AccountManagerSerivce 3580 * will always overrides account type to the session bundle before encrypting it. 3581 */ 3582 public void testFinishSessionFromStartAddAccountAccountTypeOverriden() 3583 throws IOException, AuthenticatorException, OperationCanceledException { 3584 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3585 SESSION_BUNDLE.putString(AccountManager.KEY_ACCOUNT_TYPE, "randomAccountType"); 3586 Bundle options = createOptionsWithAccountName(accountName); 3587 3588 // First get an encrypted session bundle from startAddAccountSession(...) 3589 Bundle resultBundle = startAddAccountSession( 3590 am, 3591 ACCOUNT_TYPE, 3592 AUTH_TOKEN_TYPE, 3593 REQUIRED_FEATURES, 3594 options, 3595 null /* activity */, 3596 null /* callback */, 3597 null /* handler */); 3598 3599 // Assert returned result 3600 // Assert that auth token was stripped. 3601 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3602 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3603 Bundle encryptedSessionBundle = resultBundle 3604 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3605 3606 resultBundle = finishSession( 3607 am, 3608 encryptedSessionBundle, 3609 null /* activity */, 3610 null /* callback */, 3611 null /* handler */); 3612 3613 // Assert parameters has been passed correctly 3614 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3615 3616 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3617 3618 // Assert returned result containing account name, correct type but not auth token type. 3619 validateAccountAndNoAuthTokenResult(resultBundle); 3620 } 3621 3622 /** 3623 * Tests a finishSession() when a different account type is added to session bundle 3624 * by startUpdateCredentialsSession(...) of authenticator. A bundle 3625 * containing account name and the correct account type should be returned as 3626 * AccountManagerSerivce will always override account type to the session bundle 3627 * before encrypting it. 3628 */ 3629 public void testFinishSessionFromStartUpdateCredentialsSessionAccountTypeOverriden() 3630 throws IOException, AuthenticatorException, OperationCanceledException { 3631 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3632 // MockAccountAuthenticator to return 3633 SESSION_BUNDLE.putString(AccountManager.KEY_ACCOUNT_TYPE, "randomAccountType"); 3634 Bundle options = createOptionsWithAccountName(accountName); 3635 3636 // First get an encrypted session bundle from startAddAccountSession(...) 3637 Bundle resultBundle = startUpdateCredentialsSession( 3638 am, 3639 ACCOUNT, 3640 AUTH_TOKEN_TYPE, 3641 options, 3642 null /* activity */, 3643 null /* callback */, 3644 null /* handler */); 3645 3646 // Assert returned result 3647 // Assert that auth token was stripped. 3648 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3649 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3650 Bundle encryptedSessionBundle = resultBundle 3651 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3652 3653 resultBundle = finishSession( 3654 am, 3655 encryptedSessionBundle, 3656 null /* activity */, 3657 null /* callback */, 3658 null /* handler */); 3659 3660 // Assert parameters has been passed correctly 3661 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3662 3663 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3664 3665 // Assert returned result containing account name, correct type but not auth token type. 3666 validateAccountAndNoAuthTokenResult(resultBundle); 3667 } 3668 3669 /** 3670 * Tests finishSession with authenticator activity started. When additional 3671 * info is needed from user for finishing the session and an Activity was 3672 * provided by caller, the resolution intent will be started automatically. 3673 * A bundle containing account name and type will be returned. 3674 */ 3675 // TODO: Either allow the system to see the activity from instant app, 3676 // Or separate the authenticator and test app to allow the instant app mode test. 3677 @AppModeFull 3678 public void testFinishSessionIntervene() 3679 throws IOException, AuthenticatorException, OperationCanceledException { 3680 String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3681 Bundle options = createOptionsWithAccountName(accountName); 3682 3683 // First get an encrypted session bundle from startAddAccountSession(...) 3684 Bundle resultBundle = startAddAccountSession( 3685 am, 3686 ACCOUNT_TYPE, 3687 AUTH_TOKEN_TYPE, 3688 REQUIRED_FEATURES, 3689 options, 3690 mActivity, 3691 null /* callback */, 3692 null /* handler */); 3693 3694 // Assert returned result 3695 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3696 // Assert that auth token was stripped. 3697 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3698 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3699 Bundle encryptedSessionBundle = resultBundle 3700 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3701 3702 resultBundle = finishSession( 3703 am, 3704 encryptedSessionBundle, 3705 mActivity, 3706 null /* callback */, 3707 null /* handler */); 3708 3709 // Assert parameters has been passed correctly 3710 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3711 3712 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3713 3714 // Assert returned result 3715 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3716 // Assert returned result containing account name, type but not auth token type. 3717 validateAccountAndNoAuthTokenResult(resultBundle); 3718 } 3719 3720 /** 3721 * Tests finishSession with KEY_INTENT returned but not started 3722 * automatically. When additional info is needed from user for finishing the 3723 * session and no Activity was provided by caller, the resolution intent 3724 * will not be started automatically. A bundle containing KEY_INTENT will be 3725 * returned instead. 3726 */ 3727 // TODO: Either allow the system to see the activity from instant app, 3728 // Or separate the authenticator and test app to allow the instant app mode test. 3729 @AppModeFull 3730 public void testFinishSessionWithReturnIntent() 3731 throws IOException, AuthenticatorException, OperationCanceledException { 3732 String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3733 Bundle options = createOptionsWithAccountName(accountName); 3734 3735 // First get an encrypted session bundle from startAddAccountSession(...) 3736 Bundle resultBundle = startAddAccountSession( 3737 am, 3738 ACCOUNT_TYPE, 3739 AUTH_TOKEN_TYPE, 3740 REQUIRED_FEATURES, 3741 options, 3742 mActivity, 3743 null /* callback */, 3744 null /* handler */); 3745 3746 // Assert returned result 3747 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3748 // Assert that auth token was stripped. 3749 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3750 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3751 Bundle encryptedSessionBundle = resultBundle 3752 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3753 3754 resultBundle = finishSession( 3755 am, 3756 encryptedSessionBundle, 3757 null /* activity */, 3758 null /* callback */, 3759 null /* handler */); 3760 3761 // Assert parameters has been passed correctly 3762 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3763 3764 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3765 3766 // Assert returned result 3767 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 3768 assertNotNull(returnIntent); 3769 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 3770 3771 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_NAME)); 3772 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_TYPE)); 3773 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3774 } 3775 3776 /** 3777 * Tests finishSession error case. AuthenticatorException is expected when 3778 * AccountManager.ERROR_CODE_INVALID_RESPONSE is returned by authenticator. 3779 */ 3780 public void testFinishSessionError() 3781 throws IOException, AuthenticatorException, OperationCanceledException { 3782 Bundle sessionBundle = new Bundle(); 3783 String accountNameForFinish = Fixtures.PREFIX_NAME_ERROR + "@" 3784 + Fixtures.SUFFIX_NAME_FIXTURE; 3785 sessionBundle.putString(Fixtures.KEY_ACCOUNT_NAME, accountNameForFinish); 3786 sessionBundle.putInt(AccountManager.KEY_ERROR_CODE, 3787 AccountManager.ERROR_CODE_INVALID_RESPONSE); 3788 sessionBundle.putString(AccountManager.KEY_ERROR_MESSAGE, ERROR_MESSAGE); 3789 3790 Bundle options = new Bundle(); 3791 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 3792 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 3793 options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); 3794 options.putAll(OPTIONS_BUNDLE); 3795 3796 // First get an encrypted session bundle from startAddAccountSession(...) 3797 Bundle resultBundle = startAddAccountSession( 3798 am, 3799 ACCOUNT_TYPE, 3800 AUTH_TOKEN_TYPE, 3801 REQUIRED_FEATURES, 3802 options, 3803 null /* activity */, 3804 null /* callback */, 3805 null /* handler */); 3806 3807 // Assert returned result 3808 // Assert that auth token was stripped. 3809 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3810 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3811 Bundle encryptedSessionBundle = resultBundle 3812 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3813 3814 try { 3815 finishSession( 3816 am, 3817 encryptedSessionBundle, 3818 null /* activity */, 3819 null /* callback */, 3820 null /* handler */); 3821 fail("finishSession should throw AuthenticatorException in error case."); 3822 } catch (AuthenticatorException e) { 3823 } 3824 } 3825 3826 /** 3827 * Tests finishSession() with callback and handler. A bundle containing 3828 * account name and type should be returned via the callback regardless of 3829 * whether a handler is provided. 3830 */ 3831 public void testFinishSessionWithCallbackAndHandler() 3832 throws IOException, AuthenticatorException, OperationCanceledException { 3833 testFinishSessionWithCallbackAndHandler(null /* handler */); 3834 testFinishSessionWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 3835 } 3836 3837 /** 3838 * Tests finishSession() with callback and handler and activity started. 3839 * When additional info is needed from user for finishing the session and an 3840 * Activity was provided by caller, the resolution intent will be started 3841 * automatically. A bundle containing account name and type will be returned 3842 * via the callback regardless of if handler is provided or now. 3843 */ 3844 // TODO: Either allow the system to see the activity from instant app, 3845 // Or separate the authenticator and test app to allow the instant app mode test. 3846 @AppModeFull 3847 public void testFinishSessionWithCallbackAndHandlerWithIntervene() 3848 throws IOException, AuthenticatorException, OperationCanceledException { 3849 testFinishSessionWithCallbackAndHandlerWithIntervene(null /* handler */); 3850 testFinishSessionWithCallbackAndHandlerWithIntervene( 3851 new Handler(Looper.getMainLooper())); 3852 } 3853 3854 /** 3855 * Tests finishSession() with callback and handler with KEY_INTENT 3856 * returned. When additional info is needed from user for finishing the 3857 * session and no Activity was provided by caller, the resolution intent 3858 * will not be started automatically. A bundle containing KEY_INTENT will be 3859 * returned instead via callback regardless of if handler is provided or not. 3860 */ 3861 // TODO: Either allow the system to see the activity from instant app, 3862 // Or separate the authenticator and test app to allow the instant app mode test. 3863 @AppModeFull 3864 public void testFinishSessionWithCallbackAndHandlerWithReturnIntent() 3865 throws IOException, AuthenticatorException, OperationCanceledException { 3866 testFinishSessionWithCallbackAndHandlerWithReturnIntent(null /* handler */); 3867 testFinishSessionWithCallbackAndHandlerWithReturnIntent( 3868 new Handler(Looper.getMainLooper())); 3869 } 3870 3871 /** 3872 * Tests finishSession() error case with callback and handler. 3873 * AuthenticatorException is expected when 3874 * AccountManager.ERROR_CODE_INVALID_RESPONSE is returned by authenticator. 3875 */ 3876 public void testFinishSessionErrorWithCallbackAndHandler() 3877 throws IOException, OperationCanceledException, AuthenticatorException { 3878 testFinishSessionErrorWithCallbackAndHandler(null /* handler */); 3879 testFinishSessionErrorWithCallbackAndHandler(new Handler(Looper.getMainLooper())); 3880 } 3881 3882 private void testFinishSessionWithCallbackAndHandler(Handler handler) 3883 throws IOException, AuthenticatorException, OperationCanceledException { 3884 final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" 3885 + Fixtures.SUFFIX_NAME_FIXTURE; 3886 Bundle options = createOptionsWithAccountName(accountName); 3887 3888 // First get an encrypted session bundle from startAddAccountSession(...) 3889 Bundle resultBundle = startAddAccountSession( 3890 am, 3891 ACCOUNT_TYPE, 3892 AUTH_TOKEN_TYPE, 3893 REQUIRED_FEATURES, 3894 options, 3895 null /* activity */, 3896 null /* callback */, 3897 null /* handler */); 3898 3899 // Assert returned result 3900 // Assert that auth token was stripped. 3901 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3902 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3903 Bundle encryptedSessionBundle = resultBundle 3904 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3905 3906 final CountDownLatch latch = new CountDownLatch(1); 3907 3908 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3909 @Override 3910 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3911 Bundle resultBundle = getResultExpectNoException(bundleFuture); 3912 3913 // Assert parameters has been passed correctly 3914 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3915 3916 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3917 3918 // Assert returned result containing account name, type but not auth token type. 3919 validateAccountAndNoAuthTokenResult(resultBundle); 3920 3921 latch.countDown(); 3922 } 3923 }; 3924 3925 finishSession(am, encryptedSessionBundle, mActivity, callback, handler); 3926 3927 // Wait with timeout for the callback to do its work 3928 waitForLatch(latch); 3929 } 3930 3931 private void testFinishSessionWithCallbackAndHandlerWithIntervene(Handler handler) 3932 throws IOException, AuthenticatorException, OperationCanceledException { 3933 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 3934 + Fixtures.SUFFIX_NAME_FIXTURE; 3935 Bundle options = createOptionsWithAccountName(accountName); 3936 3937 // First get an encrypted session bundle from startAddAccountSession(...) 3938 Bundle resultBundle = startAddAccountSession( 3939 am, 3940 ACCOUNT_TYPE, 3941 AUTH_TOKEN_TYPE, 3942 REQUIRED_FEATURES, 3943 options, 3944 mActivity, 3945 null /* callback */, 3946 null /* handler */); 3947 3948 // Assert returned result 3949 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3950 // Assert that auth token was stripped. 3951 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 3952 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 3953 Bundle encryptedSessionBundle = resultBundle 3954 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 3955 3956 final CountDownLatch latch = new CountDownLatch(1); 3957 3958 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 3959 @Override 3960 public void run(AccountManagerFuture<Bundle> bundleFuture) { 3961 Bundle resultBundle = getResultExpectNoException(bundleFuture); 3962 3963 // Assert parameters has been passed correctly 3964 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 3965 3966 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 3967 3968 // Assert returned result 3969 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 3970 // Assert returned result containing account name, type but not auth token type. 3971 validateAccountAndNoAuthTokenResult(resultBundle); 3972 3973 latch.countDown(); 3974 } 3975 }; 3976 3977 finishSession(am, encryptedSessionBundle, mActivity, callback, handler); 3978 3979 // Wait with timeout for the callback to do its work 3980 waitForLatch(latch); 3981 } 3982 3983 private void testFinishSessionWithCallbackAndHandlerWithReturnIntent(Handler handler) 3984 throws IOException, AuthenticatorException, OperationCanceledException { 3985 final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@" 3986 + Fixtures.SUFFIX_NAME_FIXTURE; 3987 Bundle options = createOptionsWithAccountName(accountName); 3988 3989 // First get an encrypted session bundle from startAddAccountSession(...) 3990 Bundle resultBundle = startAddAccountSession( 3991 am, 3992 ACCOUNT_TYPE, 3993 AUTH_TOKEN_TYPE, 3994 REQUIRED_FEATURES, 3995 options, 3996 mActivity, 3997 null /* callback */, 3998 null /* handler */); 3999 4000 // Assert returned result 4001 assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT)); 4002 // Assert that auth token was stripped. 4003 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 4004 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 4005 Bundle encryptedSessionBundle = resultBundle 4006 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 4007 4008 final CountDownLatch latch = new CountDownLatch(1); 4009 4010 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 4011 @Override 4012 public void run(AccountManagerFuture<Bundle> bundleFuture) { 4013 Bundle resultBundle = getResultExpectNoException(bundleFuture); 4014 4015 // Assert parameters has been passed correctly 4016 assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType()); 4017 4018 validateFinishSessionOptions(accountName, SESSION_BUNDLE); 4019 4020 // Assert returned result 4021 Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT); 4022 assertNotNull(returnIntent); 4023 assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT)); 4024 4025 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_NAME)); 4026 assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_TYPE)); 4027 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 4028 4029 latch.countDown(); 4030 } 4031 }; 4032 4033 finishSession(am, encryptedSessionBundle, null, callback, handler); 4034 4035 // Wait with timeout for the callback to do its work 4036 waitForLatch(latch); 4037 } 4038 4039 private void testFinishSessionErrorWithCallbackAndHandler(Handler handler) 4040 throws IOException, OperationCanceledException, AuthenticatorException { 4041 Bundle sessionBundle = new Bundle(); 4042 String accountNameForFinish = Fixtures.PREFIX_NAME_ERROR + "@" 4043 + Fixtures.SUFFIX_NAME_FIXTURE; 4044 sessionBundle.putString(Fixtures.KEY_ACCOUNT_NAME, accountNameForFinish); 4045 sessionBundle.putInt(AccountManager.KEY_ERROR_CODE, 4046 AccountManager.ERROR_CODE_INVALID_RESPONSE); 4047 sessionBundle.putString(AccountManager.KEY_ERROR_MESSAGE, ERROR_MESSAGE); 4048 4049 Bundle options = new Bundle(); 4050 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4051 options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName); 4052 options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); 4053 options.putAll(OPTIONS_BUNDLE); 4054 4055 // First get an encrypted session bundle from startAddAccountSession(...) 4056 Bundle resultBundle = startAddAccountSession( 4057 am, 4058 ACCOUNT_TYPE, 4059 AUTH_TOKEN_TYPE, 4060 REQUIRED_FEATURES, 4061 options, 4062 null /* activity */, 4063 null /* callback */, 4064 null /* handler */); 4065 4066 // Assert returned result 4067 // Assert that auth token was stripped. 4068 assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN)); 4069 validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle); 4070 Bundle encryptedSessionBundle = resultBundle 4071 .getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); 4072 4073 final CountDownLatch latch = new CountDownLatch(1); 4074 4075 AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { 4076 @Override 4077 public void run(AccountManagerFuture<Bundle> bundleFuture) { 4078 try { 4079 bundleFuture.getResult(); 4080 fail("should have thrown an AuthenticatorException"); 4081 } catch (OperationCanceledException e) { 4082 fail("should not throw an OperationCanceledException"); 4083 } catch (IOException e) { 4084 fail("should not throw an IOException"); 4085 } catch (AuthenticatorException e) { 4086 latch.countDown(); 4087 } 4088 } 4089 }; 4090 4091 try { 4092 finishSession(am, encryptedSessionBundle, mActivity, callback, handler); 4093 fail("should have thrown an AuthenticatorException"); 4094 } catch (AuthenticatorException e1) { 4095 } 4096 4097 // Wait with timeout for the callback to do its work 4098 waitForLatch(latch); 4099 } 4100 4101 private Bundle finishSession(AccountManager am, Bundle sessionBundle, Activity activity, 4102 AccountManagerCallback<Bundle> callback, Handler handler) 4103 throws IOException, AuthenticatorException, OperationCanceledException { 4104 // Cleanup before calling finishSession(...) with the encrypted session bundle. 4105 mockAuthenticator.clearData(); 4106 4107 AccountManagerFuture<Bundle> futureBundle = am.finishSession( 4108 sessionBundle, 4109 activity, 4110 callback, 4111 handler); 4112 4113 Bundle resultBundle = futureBundle.getResult(); 4114 assertTrue(futureBundle.isDone()); 4115 assertNotNull(resultBundle); 4116 4117 return resultBundle; 4118 } 4119 4120 private void validateFinishSessionOptions(String accountName, Bundle options) { 4121 validateOptions(options, mockAuthenticator.mOptionsFinishSession); 4122 assertNotNull(mockAuthenticator.mOptionsFinishSession); 4123 assertEquals(ACCOUNT_TYPE, mockAuthenticator.mOptionsFinishSession 4124 .getString(AccountManager.KEY_ACCOUNT_TYPE)); 4125 assertEquals(accountName, 4126 mockAuthenticator.mOptionsFinishSession.getString(Fixtures.KEY_ACCOUNT_NAME)); 4127 4128 validateSystemOptions(mockAuthenticator.mOptionsFinishSession); 4129 validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials); 4130 validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials); 4131 validateOptions(null, mockAuthenticator.mOptionsGetAuthToken); 4132 validateOptions(null, mockAuthenticator.mOptionsAddAccount); 4133 validateOptions(null, mockAuthenticator.mOptionsStartAddAccountSession); 4134 validateOptions(null, mockAuthenticator.mOptionsStartUpdateCredentialsSession); 4135 } 4136 4137 /** 4138 * Tests a basic isCredentialsUpdateSuggested() which returns a bundle containing boolean true. 4139 */ 4140 public void testIsCredentialsUpdateSuggested_Success() 4141 throws IOException, AuthenticatorException, OperationCanceledException { 4142 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4143 Account account = new Account(accountName, ACCOUNT_TYPE); 4144 4145 Boolean result = isCredentialsUpdateSuggested( 4146 am, 4147 account, 4148 ACCOUNT_STATUS_TOKEN, 4149 null /* callback */, 4150 null /* handler */); 4151 4152 // Assert parameters has been passed correctly 4153 validateIsCredentialsUpdateSuggestedParametersAndOptions(account); 4154 4155 // Assert returned result 4156 assertTrue(result); 4157 } 4158 4159 /** 4160 * Tests isCredentialsUpdateSuggested() when account is null. 4161 * It should throw IllegalArgumentationException. 4162 */ 4163 public void testIsCredentialsUpdateSuggestedNullAccount_IllegalArgumentationException() 4164 throws IOException, AuthenticatorException, OperationCanceledException { 4165 4166 try { 4167 isCredentialsUpdateSuggested( 4168 am, 4169 null /* account */, 4170 ACCOUNT_STATUS_TOKEN, 4171 null /* callback */, 4172 null /* handler */); 4173 fail("Should have thrown IllegalArgumentation when calling with null account!"); 4174 } catch (IllegalArgumentException e) { 4175 } 4176 } 4177 4178 /** 4179 * Tests isCredentialsUpdateSuggested() when statusToken is empty. 4180 * It should throw IllegalArgumentationException. 4181 */ 4182 public void testIsCredentialsUpdateSuggestedEmptyToken_IllegalArgumentationException() 4183 throws IOException, AuthenticatorException, OperationCanceledException { 4184 4185 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4186 Account account = new Account(accountName, ACCOUNT_TYPE); 4187 try { 4188 isCredentialsUpdateSuggested( 4189 am, 4190 account, 4191 "" /* statusToken */, 4192 null /* callback */, 4193 null /* handler */); 4194 fail("Should have thrown IllegalArgumentation when calling with empty statusToken!"); 4195 } catch (IllegalArgumentException e) { 4196 } 4197 } 4198 4199 /** 4200 * Tests isCredentialsUpdateSuggested() error case. AuthenticatorException is expected when 4201 * authenticator return {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 4202 */ 4203 public void testIsCredentialsUpdateSuggested_Error() 4204 throws IOException, AuthenticatorException, OperationCanceledException { 4205 String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4206 Account account = new Account(accountName, ACCOUNT_TYPE); 4207 4208 try { 4209 isCredentialsUpdateSuggested( 4210 am, 4211 account, 4212 ACCOUNT_STATUS_TOKEN, 4213 null /* callback */, 4214 null /* handler */); 4215 fail("Should have thrown AuthenticatorException in error case."); 4216 } catch (AuthenticatorException e) { 4217 } 4218 } 4219 4220 /** 4221 * Tests isCredentialsUpdateSuggested() with callback and handler. A boolean should be included 4222 * in the result. Callback should be triggered with the result regardless of a handler is 4223 * provided or not. 4224 */ 4225 public void testIsCredentialsUpdateSuggestedWithCallbackAndHandler() 4226 throws IOException, AuthenticatorException, OperationCanceledException { 4227 testIsCredentialsUpdateSuggestedWithCallbackAndHandler(null /* handler */); 4228 testIsCredentialsUpdateSuggestedWithCallbackAndHandler( 4229 new Handler(Looper.getMainLooper())); 4230 } 4231 4232 /** 4233 * Tests isCredentialsUpdateSuggested() error case with callback and handler. 4234 * AuthenticatorException is expected when authenticator return 4235 * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code. 4236 */ 4237 public void testIsCredentialsUpdateSuggestedErrorWithCallbackAndHandler() 4238 throws IOException, OperationCanceledException, AuthenticatorException { 4239 testIsCredentialsUpdateSuggestedErrorWithCallbackAndHandler(null /* handler */); 4240 testIsCredentialsUpdateSuggestedErrorWithCallbackAndHandler( 4241 new Handler(Looper.getMainLooper())); 4242 } 4243 4244 private void testIsCredentialsUpdateSuggestedWithCallbackAndHandler(Handler handler) 4245 throws IOException, AuthenticatorException, OperationCanceledException { 4246 String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4247 final Account account = new Account(accountName, ACCOUNT_TYPE); 4248 final CountDownLatch latch = new CountDownLatch(1); 4249 4250 AccountManagerCallback<Boolean> callback = new AccountManagerCallback<Boolean>() { 4251 @Override 4252 public void run(AccountManagerFuture<Boolean> booleanFuture) { 4253 Boolean result = false; 4254 try { 4255 result = booleanFuture.getResult(); 4256 } catch (OperationCanceledException e) { 4257 fail("should not throw an OperationCanceledException"); 4258 } catch (IOException e) { 4259 fail("should not throw an IOException"); 4260 } catch (AuthenticatorException e) { 4261 fail("should not throw an AuthenticatorException"); 4262 } 4263 4264 // Assert parameters has been passed correctly 4265 validateIsCredentialsUpdateSuggestedParametersAndOptions(account); 4266 4267 // Assert returned result 4268 assertTrue(result); 4269 4270 latch.countDown(); 4271 } 4272 }; 4273 4274 isCredentialsUpdateSuggested( 4275 am, 4276 account, 4277 ACCOUNT_STATUS_TOKEN, 4278 callback, 4279 handler); 4280 4281 // Wait with timeout for the callback to do its work 4282 waitForLatch(latch); 4283 } 4284 4285 private void testIsCredentialsUpdateSuggestedErrorWithCallbackAndHandler(Handler handler) 4286 throws IOException, OperationCanceledException, AuthenticatorException { 4287 String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE; 4288 final Account account = new Account(accountName, ACCOUNT_TYPE); 4289 4290 final CountDownLatch latch = new CountDownLatch(1); 4291 4292 AccountManagerCallback<Boolean> callback = new AccountManagerCallback<Boolean>() { 4293 @Override 4294 public void run(AccountManagerFuture<Boolean> booleanFuture) { 4295 try { 4296 booleanFuture.getResult(); 4297 // AuthenticatorException should be thrown when authenticator 4298 // returns AccountManager.ERROR_CODE_INVALID_RESPONSE. 4299 fail("should have thrown an AuthenticatorException"); 4300 } catch (OperationCanceledException e) { 4301 fail("should not throw an OperationCanceledException"); 4302 } catch (IOException e) { 4303 fail("should not throw an IOException"); 4304 } catch (AuthenticatorException e) { 4305 // Test passed as AuthenticatorException is expected. 4306 } finally { 4307 latch.countDown(); 4308 } 4309 } 4310 }; 4311 4312 isCredentialsUpdateSuggested( 4313 am, 4314 account, 4315 ACCOUNT_STATUS_TOKEN, 4316 callback, 4317 handler); 4318 4319 // Wait with timeout for the callback to do its work 4320 waitForLatch(latch); 4321 } 4322 4323 private Boolean isCredentialsUpdateSuggested( 4324 AccountManager am, 4325 Account account, 4326 String statusToken, 4327 AccountManagerCallback<Boolean> callback, 4328 Handler handler) 4329 throws IOException, AuthenticatorException, OperationCanceledException { 4330 4331 AccountManagerFuture<Boolean> booleanFuture = am.isCredentialsUpdateSuggested( 4332 account, 4333 statusToken, 4334 callback, 4335 handler); 4336 4337 Boolean result = false; 4338 if (callback == null) { 4339 result = booleanFuture.getResult(); 4340 assertTrue(booleanFuture.isDone()); 4341 } 4342 4343 return result; 4344 } 4345 4346 private void verifyAccountsGroupedByType(Account[] accounts) { 4347 4348 Map<String, Integer> firstPositionForType = new HashMap<>(); 4349 Map<String, Integer> lastPositionForType = new HashMap<>(); 4350 Map<String, Integer> counterForType = new HashMap<>(); 4351 for (int i = 0; i < accounts.length; i++) { 4352 String type = accounts[i].type; 4353 4354 Integer first = firstPositionForType.get(type); 4355 first = first != null ? first : Integer.MAX_VALUE; 4356 firstPositionForType.put(type, Math.min(first, i)); 4357 4358 Integer last = lastPositionForType.get(type); 4359 last = last != null ? last : Integer.MIN_VALUE; 4360 lastPositionForType.put(type, Math.max(last, i)); 4361 4362 Integer counter = counterForType.get(type); 4363 counter = counter != null ? counter : 0; 4364 counterForType.put(type, counter + 1); 4365 } 4366 for (String type : counterForType.keySet()) { 4367 assertEquals((int)lastPositionForType.get(type), 4368 firstPositionForType.get(type) + counterForType.get(type) - 1); 4369 } 4370 } 4371 } 4372