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