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