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