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