blob: 0e84fb91fea27217f91a2f91703ab0dc27481e25 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.accounts.cts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorDescription;
import android.accounts.AuthenticatorException;
import android.accounts.OnAccountsUpdateListener;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.StrictMode;
import android.test.ActivityInstrumentationTestCase2;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* You can run those unit tests with the following command line:
*
* adb shell am instrument
* -e debug false -w
* -e class android.accounts.cts.AccountManagerTest
* android.accounts.cts/android.support.test.runner.AndroidJUnitRunner
*/
public class AccountManagerTest extends ActivityInstrumentationTestCase2<AccountDummyActivity> {
public static final String ACCOUNT_NAME = "android.accounts.cts.account.name";
public static final String ACCOUNT_NEW_NAME = "android.accounts.cts.account.name.rename";
public static final String ACCOUNT_NAME_OTHER = "android.accounts.cts.account.name.other";
public static final String ACCOUNT_TYPE = "android.accounts.cts.account.type";
public static final String ACCOUNT_TYPE_CUSTOM = "android.accounts.cts.custom.account.type";
public static final String ACCOUNT_TYPE_ABSENT = "android.accounts.cts.account.type.absent";
public static final String ACCOUNT_PASSWORD = "android.accounts.cts.account.password";
public static final String AUTH_TOKEN_TYPE = "mockAuthTokenType";
public static final String AUTH_EXPIRING_TOKEN_TYPE = "mockAuthExpiringTokenType";
public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel";
public static final long AUTH_TOKEN_DURATION_MILLIS = 10000L; // Ten seconds.
public static final String FEATURE_1 = "feature.1";
public static final String FEATURE_2 = "feature.2";
public static final String NON_EXISTING_FEATURE = "feature.3";
public static final String OPTION_NAME_1 = "option.name.1";
public static final String OPTION_VALUE_1 = "option.value.1";
public static final String OPTION_NAME_2 = "option.name.2";
public static final String OPTION_VALUE_2 = "option.value.2";
public static final String[] REQUIRED_FEATURES = new String[] { FEATURE_1, FEATURE_2 };
public static final Bundle OPTIONS_BUNDLE = new Bundle();
public static final Bundle USERDATA_BUNDLE = new Bundle();
public static final String USERDATA_NAME_1 = "user.data.name.1";
public static final String USERDATA_NAME_2 = "user.data.name.2";
public static final String USERDATA_VALUE_1 = "user.data.value.1";
public static final String USERDATA_VALUE_2 = "user.data.value.2";
public static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
public static final Account ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API = new Account(
MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE);
public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE);
public static final Account CUSTOM_TOKEN_ACCOUNT =
new Account(ACCOUNT_NAME,ACCOUNT_TYPE_CUSTOM);
private static MockAccountAuthenticator mockAuthenticator;
private static final int LATCH_TIMEOUT_MS = 500;
private static AccountManager am;
public synchronized static MockAccountAuthenticator getMockAuthenticator(Context context) {
if (null == mockAuthenticator) {
mockAuthenticator = new MockAccountAuthenticator(context);
}
return mockAuthenticator;
}
private Activity mActivity;
private Context mContext;
public AccountManagerTest() {
super(AccountDummyActivity.class);
}
@Override
public void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
mContext = getInstrumentation().getTargetContext();
OPTIONS_BUNDLE.putString(OPTION_NAME_1, OPTION_VALUE_1);
OPTIONS_BUNDLE.putString(OPTION_NAME_2, OPTION_VALUE_2);
USERDATA_BUNDLE.putString(USERDATA_NAME_1, USERDATA_VALUE_1);
getMockAuthenticator(mContext);
am = AccountManager.get(mContext);
}
@Override
public void tearDown() throws Exception, AuthenticatorException, OperationCanceledException {
mockAuthenticator.clearData();
// Need to clean up created account
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
// Clean out any other accounts added during the tests.
Account[] ctsAccounts = am.getAccountsByType(ACCOUNT_TYPE);
Account[] ctsCustomAccounts = am.getAccountsByType(ACCOUNT_TYPE_CUSTOM);
ArrayList<Account> accounts = new ArrayList<>(Arrays.asList(ctsAccounts));
accounts.addAll(Arrays.asList(ctsCustomAccounts));
for (Account ctsAccount : accounts) {
removeAccount(am, ctsAccount, mActivity, null /* callback */);
}
// need to clean up the authenticator cached data
mockAuthenticator.clearData();
super.tearDown();
}
interface TokenFetcher {
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException;
public Account getAccount();
}
private void validateSuccessfulTokenFetchingLifecycle(TokenFetcher fetcher, String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
Account account = fetcher.getAccount();
Bundle expected = new Bundle();
expected.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
expected.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
// First fetch.
Bundle actual = fetcher.fetch(tokenType);
assertTrue(mockAuthenticator.isRecentlyCalled());
validateAccountAndAuthTokenResult(expected, actual);
/*
* On the second fetch the cache will be populated if we are using a authenticator with
* customTokens=false or we are using a scope that will cause the authenticator to set an
* expiration time (and that expiration time hasn't been reached).
*/
actual = fetcher.fetch(tokenType);
boolean isCachingExpected =
ACCOUNT_TYPE.equals(account.type) || AUTH_EXPIRING_TOKEN_TYPE.equals(tokenType);
assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled());
validateAccountAndAuthTokenResult(expected, actual);
try {
// Delay further execution until expiring tokens can actually expire.
Thread.sleep(mockAuthenticator.getTokenDurationMillis() + 1L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
/*
* With the time shift above, the third request will result in cache hits only from
* customToken=false authenticators.
*/
actual = fetcher.fetch(tokenType);
isCachingExpected = ACCOUNT_TYPE.equals(account.type);
assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled());
validateAccountAndAuthTokenResult(expected, actual);
// invalidate token
String token = actual.getString(AccountManager.KEY_AUTHTOKEN);
am.invalidateAuthToken(account.type, token);
/*
* Upon invalidating the token, the cache should be clear regardless of authenticator.
*/
actual = fetcher.fetch(tokenType);
assertTrue(mockAuthenticator.isRecentlyCalled());
validateAccountAndAuthTokenResult(expected, actual);
}
private void validateAccountAndAuthTokenResult(Bundle actual) {
assertEquals(
ACCOUNT.name,
actual.get(AccountManager.KEY_ACCOUNT_NAME));
assertEquals(
ACCOUNT.type,
actual.get(AccountManager.KEY_ACCOUNT_TYPE));
assertEquals(
mockAuthenticator.getLastTokenServed(),
actual.get(AccountManager.KEY_AUTHTOKEN));
}
private void validateAccountAndAuthTokenResult(Bundle expected, Bundle actual) {
assertEquals(
expected.get(AccountManager.KEY_ACCOUNT_NAME),
actual.get(AccountManager.KEY_ACCOUNT_NAME));
assertEquals(
expected.get(AccountManager.KEY_ACCOUNT_TYPE),
actual.get(AccountManager.KEY_ACCOUNT_TYPE));
assertEquals(
mockAuthenticator.getLastTokenServed(),
actual.get(AccountManager.KEY_AUTHTOKEN));
}
private void validateAccountAndNoAuthTokenResult(Bundle result) {
assertEquals(ACCOUNT_NAME, result.get(AccountManager.KEY_ACCOUNT_NAME));
assertEquals(ACCOUNT_TYPE, result.get(AccountManager.KEY_ACCOUNT_TYPE));
assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
}
private void validateNullResult(Bundle resultBundle) {
assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_NAME));
assertNull(resultBundle.get(AccountManager.KEY_ACCOUNT_TYPE));
assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
}
private void validateAccountAndAuthTokenType() {
assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType());
assertEquals(AUTH_TOKEN_TYPE, mockAuthenticator.getAuthTokenType());
}
private void validateFeatures() {
assertEquals(REQUIRED_FEATURES[0], mockAuthenticator.getRequiredFeatures()[0]);
assertEquals(REQUIRED_FEATURES[1], mockAuthenticator.getRequiredFeatures()[1]);
}
private void validateOptions(Bundle expectedOptions, Bundle actualOptions) {
// In ICS AccountManager may add options to indicate the caller id.
// We only validate that the passed in options are present in the actual ones
if (expectedOptions != null) {
assertNotNull(actualOptions);
assertEquals(expectedOptions.get(OPTION_NAME_1), actualOptions.get(OPTION_NAME_1));
assertEquals(expectedOptions.get(OPTION_NAME_2), actualOptions.get(OPTION_NAME_2));
}
}
private void validateSystemOptions(Bundle options) {
assertNotNull(options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME));
assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID));
assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID));
}
private void validateCredentials() {
assertEquals(ACCOUNT, mockAuthenticator.getAccount());
}
private int getAccountsCount() {
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
return accounts.length;
}
private Bundle addAccount(AccountManager am, String accountType, String authTokenType,
String[] requiredFeatures, Bundle options, Activity activity,
AccountManagerCallback<Bundle> callback, Handler handler) throws
IOException, AuthenticatorException, OperationCanceledException {
AccountManagerFuture<Bundle> futureBundle = am.addAccount(
accountType,
authTokenType,
requiredFeatures,
options,
activity,
callback,
handler);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
return resultBundle;
}
private Account renameAccount(AccountManager am, Account account, String newName)
throws OperationCanceledException, AuthenticatorException, IOException {
AccountManagerFuture<Account> futureAccount = am.renameAccount(
account, newName, null /* callback */, null /* handler */);
Account renamedAccount = futureAccount.getResult();
assertTrue(futureAccount.isDone());
assertNotNull(renamedAccount);
return renamedAccount;
}
private boolean removeAccount(AccountManager am, Account account,
AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException,
OperationCanceledException {
AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account,
callback,
null /* handler */);
Boolean resultBoolean = futureBoolean.getResult();
assertTrue(futureBoolean.isDone());
return resultBoolean;
}
private Bundle removeAccountWithIntentLaunch(AccountManager am, Account account,
Activity activity, AccountManagerCallback<Bundle> callback) throws IOException,
AuthenticatorException, OperationCanceledException {
AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
activity,
callback,
null /* handler */);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
return resultBundle;
}
private Bundle removeAccount(AccountManager am, Account account, Activity activity,
AccountManagerCallback<Bundle> callback) throws IOException, AuthenticatorException,
OperationCanceledException {
AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
activity,
callback,
null /* handler */);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
return resultBundle;
}
private boolean removeAccountExplicitly(AccountManager am, Account account) {
return am.removeAccountExplicitly(account);
}
private void addAccountExplicitly(Account account, String password, Bundle userdata) {
assertTrue(am.addAccountExplicitly(account, password, userdata));
}
private Bundle getAuthTokenByFeature(String[] features, Activity activity)
throws IOException, AuthenticatorException, OperationCanceledException {
AccountManagerFuture<Bundle> futureBundle = am.getAuthTokenByFeatures(ACCOUNT_TYPE,
AUTH_TOKEN_TYPE,
features,
activity,
OPTIONS_BUNDLE,
OPTIONS_BUNDLE,
null /* no callback */,
null /* no handler */
);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
return resultBundle;
}
private boolean isAccountPresent(Account[] accounts, Account accountToCheck) {
if (null == accounts || null == accountToCheck) {
return false;
}
boolean result = false;
int length = accounts.length;
for (int n=0; n<length; n++) {
if(accountToCheck.equals(accounts[n])) {
result = true;
break;
}
}
return result;
}
/**
* Test singleton
*/
public void testGet() {
assertNotNull(AccountManager.get(mContext));
}
/**
* Test a basic addAccount()
*/
public void testAddAccount() throws IOException, AuthenticatorException,
OperationCanceledException {
Bundle resultBundle = addAccount(am,
ACCOUNT_TYPE,
AUTH_TOKEN_TYPE,
REQUIRED_FEATURES,
OPTIONS_BUNDLE,
mActivity,
null /* callback */,
null /* handler */);
// Assert parameters has been passed correctly
validateAccountAndAuthTokenType();
validateFeatures();
validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount);
validateSystemOptions(mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
// Assert returned result
validateAccountAndNoAuthTokenResult(resultBundle);
}
/**
* Test addAccount() with callback and handler
*/
public void testAddAccountWithCallbackAndHandler() throws IOException,
AuthenticatorException, OperationCanceledException {
testAddAccountWithCallbackAndHandler(null /* handler */);
testAddAccountWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testAddAccountWithCallbackAndHandler(Handler handler) throws IOException,
AuthenticatorException, OperationCanceledException {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
}
// Assert parameters has been passed correctly
validateAccountAndAuthTokenType();
validateFeatures();
validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
// Assert return result
validateAccountAndNoAuthTokenResult(resultBundle);
latch.countDown();
}
};
addAccount(am,
ACCOUNT_TYPE,
AUTH_TOKEN_TYPE,
REQUIRED_FEATURES,
OPTIONS_BUNDLE,
mActivity,
callback,
handler);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test addAccountExplicitly(), renameAccount() and removeAccount().
*/
public void testAddAccountExplicitlyAndRemoveAccount() throws IOException,
AuthenticatorException, OperationCanceledException {
final int expectedAccountsCount = getAccountsCount();
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Assert that we have one more account
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
// Need to clean up
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
// and verify that we go back to the initial state
accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(expectedAccountsCount, accounts.length);
}
/**
* Test addAccountExplicitly(), renameAccount() and removeAccount().
*/
public void testAddAccountExplicitlyAndRemoveAccountWithNewApi() throws IOException,
AuthenticatorException, OperationCanceledException {
final int expectedAccountsCount = getAccountsCount();
addAccountExplicitly(ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, ACCOUNT_PASSWORD, null /* userData */);
// Assert that we have one more account
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
// Deprecated API should not work
assertFalse(removeAccount(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, null /* callback */));
accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
// Check removal of account
assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, mActivity, null /* callback */)
.getBoolean(AccountManager.KEY_BOOLEAN_RESULT));
// and verify that we go back to the initial state
accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(expectedAccountsCount, accounts.length);
}
/**
* Test addAccountExplicitly(), renameAccount() and removeAccount().
*/
public void testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi() throws IOException,
AuthenticatorException, OperationCanceledException {
final int expectedAccountsCount = getAccountsCount();
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Assert that we have one more account
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
// Need to clean up
assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
// and verify that we go back to the initial state
accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(expectedAccountsCount, accounts.length);
}
/**
* Test addAccountExplicitly() and removeAccountExplictly().
*/
public void testAddAccountExplicitlyAndRemoveAccountExplicitly() {
final int expectedAccountsCount = getAccountsCount();
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Assert that we have one more account
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
// Need to clean up
assertTrue(removeAccountExplicitly(am, ACCOUNT));
// and verify that we go back to the initial state
accounts = am.getAccounts();
assertNotNull(accounts);
assertEquals(expectedAccountsCount, accounts.length);
}
/**
* Test setUserData() and getUserData().
*/
public void testAccountRenameAndGetPreviousName()
throws OperationCanceledException, AuthenticatorException, IOException {
// Add a first account
boolean result = am.addAccountExplicitly(ACCOUNT,
ACCOUNT_PASSWORD,
USERDATA_BUNDLE);
assertTrue(result);
// Prior to a renmae, the previous name should be null.
String nullName = am.getPreviousName(ACCOUNT);
assertNull(nullName);
final int expectedAccountsCount = getAccountsCount();
Account renamedAccount = renameAccount(am, ACCOUNT, ACCOUNT_NEW_NAME);
/*
* Make sure that the resultant renamed account has the correct name
* and is associated with the correct account type.
*/
assertEquals(ACCOUNT_NEW_NAME, renamedAccount.name);
assertEquals(ACCOUNT.type, renamedAccount.type);
// Make sure the total number of accounts is the same.
Account[] accounts = am.getAccounts();
assertEquals(expectedAccountsCount, accounts.length);
// Make sure the old account isn't present.
assertFalse(isAccountPresent(am.getAccounts(), ACCOUNT));
// But that the new one is.
assertTrue(isAccountPresent(am.getAccounts(), renamedAccount));
// Check that the UserData is still present.
assertEquals(USERDATA_VALUE_1, am.getUserData(renamedAccount, USERDATA_NAME_1));
assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount));
// Need to clean up
assertTrue(removeAccount(am, renamedAccount, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
}
/**
* Test getAccounts() and getAccountsByType()
*/
public void testGetAccountsAndGetAccountsByType() {
assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT));
assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT_SAME_TYPE));
final int accountsCount = getAccountsCount();
// Add a first account
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Check that we have the new account
Account[] accounts = am.getAccounts();
assertEquals(1 + accountsCount, accounts.length);
assertEquals(true, isAccountPresent(accounts, ACCOUNT));
// Add another account
addAccountExplicitly(ACCOUNT_SAME_TYPE, ACCOUNT_PASSWORD, null /* userData */);
// Check that we have one more account again
accounts = am.getAccounts();
assertEquals(2 + accountsCount, accounts.length);
assertEquals(true, isAccountPresent(accounts, ACCOUNT_SAME_TYPE));
// Check if we have one from first type
accounts = am.getAccountsByType(ACCOUNT_TYPE);
assertEquals(2, accounts.length);
// Check if we dont have any account from the other type
accounts = am.getAccountsByType(ACCOUNT_TYPE_ABSENT);
assertEquals(0, accounts.length);
}
/**
* Test getAuthenticatorTypes()
*/
public void testGetAuthenticatorTypes() {
AuthenticatorDescription[] types = am.getAuthenticatorTypes();
for(AuthenticatorDescription description: types) {
if (description.type.equals(ACCOUNT_TYPE)) {
return;
}
}
fail("should have found Authenticator type: " + ACCOUNT_TYPE);
}
/**
* Test setPassword() and getPassword()
*/
public void testSetAndGetAndClearPassword() {
// Add a first account
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Check that the password is the one we defined
assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT));
// Clear the password and check that it is cleared
am.clearPassword(ACCOUNT);
assertNull(am.getPassword(ACCOUNT));
// Reset the password
am.setPassword(ACCOUNT, ACCOUNT_PASSWORD);
// Check that the password is the one we defined
assertEquals(ACCOUNT_PASSWORD, am.getPassword(ACCOUNT));
}
/**
* Test setUserData() and getUserData()
*/
public void testSetAndGetUserData() {
// Add a first account
boolean result = am.addAccountExplicitly(ACCOUNT,
ACCOUNT_PASSWORD,
USERDATA_BUNDLE);
assertTrue(result);
// Check that the UserData is the one we defined
assertEquals(USERDATA_VALUE_1, am.getUserData(ACCOUNT, USERDATA_NAME_1));
am.setUserData(ACCOUNT, USERDATA_NAME_2, USERDATA_VALUE_2);
// Check that the UserData is the one we defined
assertEquals(USERDATA_VALUE_2, am.getUserData(ACCOUNT, USERDATA_NAME_2));
}
/**
* Test getAccountsByTypeAndFeatures()
*/
public void testGetAccountsByTypeAndFeatures() throws IOException,
AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures(
ACCOUNT_TYPE, REQUIRED_FEATURES, null, null);
Account[] accounts = futureAccounts.getResult();
assertNotNull(accounts);
assertEquals(1, accounts.length);
assertEquals(true, isAccountPresent(accounts, ACCOUNT));
futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE,
new String[] { NON_EXISTING_FEATURE },
null /* callback*/,
null /* handler */);
accounts = futureAccounts.getResult();
assertNotNull(accounts);
assertEquals(0, accounts.length);
}
/**
* Test getAccountsByTypeAndFeatures() with callback and handler
*/
public void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler() throws IOException,
AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(null /* handler */);
testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testGetAccountsByTypeAndFeaturesWithCallbackAndHandler(Handler handler) throws
IOException, AuthenticatorException, OperationCanceledException {
final CountDownLatch latch1 = new CountDownLatch(1);
AccountManagerCallback<Account[]> callback1 = new AccountManagerCallback<Account[]>() {
@Override
public void run(AccountManagerFuture<Account[]> accountsFuture) {
try {
Account[] accounts = accountsFuture.getResult();
assertNotNull(accounts);
assertEquals(1, accounts.length);
assertEquals(true, isAccountPresent(accounts, ACCOUNT));
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
} finally {
latch1.countDown();
}
}
};
AccountManagerFuture<Account[]> futureAccounts = am.getAccountsByTypeAndFeatures(
ACCOUNT_TYPE,
REQUIRED_FEATURES,
callback1,
handler);
Account[] accounts = futureAccounts.getResult();
assertNotNull(accounts);
assertEquals(1, accounts.length);
assertEquals(true, isAccountPresent(accounts, ACCOUNT));
// Wait with timeout for the callback to do its work
try {
latch1.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
final CountDownLatch latch2 = new CountDownLatch(1);
AccountManagerCallback<Account[]> callback2 = new AccountManagerCallback<Account[]>() {
@Override
public void run(AccountManagerFuture<Account[]> accountsFuture) {
try {
Account[] accounts = accountsFuture.getResult();
assertNotNull(accounts);
assertEquals(0, accounts.length);
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
} finally {
latch2.countDown();
}
}
};
accounts = null;
futureAccounts = am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE,
new String[] { NON_EXISTING_FEATURE },
callback2,
handler);
accounts = futureAccounts.getResult();
assertNotNull(accounts);
assertEquals(0, accounts.length);
// Wait with timeout for the callback to do its work
try {
latch2.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test setAuthToken() and peekAuthToken()
*/
public void testSetAndPeekAndInvalidateAuthToken() {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
String expected = "x";
am.setAuthToken(ACCOUNT, AUTH_TOKEN_TYPE, expected);
// Ask for the AuthToken
String token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE);
assertNotNull(token);
assertEquals(expected, token);
am.invalidateAuthToken(ACCOUNT_TYPE, token);
token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE);
assertNull(token);
}
/**
* Test successful blockingGetAuthToken() with customTokens=false authenticator.
*/
public void testBlockingGetAuthToken_DefaultToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null);
String token = am.blockingGetAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
false /* no failure notification */);
// Ask for the AuthToken
assertNotNull(token);
assertEquals(mockAuthenticator.getLastTokenServed(), token);
}
private static class BlockingGetAuthTokenFetcher implements TokenFetcher {
private final Account mAccount;
BlockingGetAuthTokenFetcher(Account account) {
mAccount = account;
}
@Override
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
String token = am.blockingGetAuthToken(
getAccount(),
tokenType,
false /* no failure notification */);
Bundle result = new Bundle();
result.putString(AccountManager.KEY_AUTHTOKEN, token);
result.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);
return result;
}
@Override
public Account getAccount() {
return CUSTOM_TOKEN_ACCOUNT;
}
}
/**
* Test successful blockingGetAuthToken() with customTokens=true authenticator.
*/
public void testBlockingGetAuthToken_CustomToken_NoCaching_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT);
validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE);
}
/**
* Test successful blockingGetAuthToken() with customTokens=true authenticator.
*/
public void testBlockingGetAuthToken_CustomToken_ExpiringCache_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT);
validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE);
}
/**
* Test successful getAuthToken() using a future with customTokens=false authenticator.
*/
public void testDeprecatedGetAuthTokenWithFuture_NoOptions_DefaultToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
false /* no failure notification */,
null /* no callback */,
null /* no handler */
);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
}
/**
* Test successful getAuthToken() using a future with customTokens=false without
* expiring tokens.
*/
public void testDeprecatedGetAuthTokenWithFuture_NoOptions_CustomToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
// validateSuccessfulTokenFetchingLifecycle(AccountManager am, TokenFetcher fetcher, String tokenType)
TokenFetcher f = new TokenFetcher() {
@Override
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(
getAccount(),
tokenType,
false /* no failure notification */,
null /* no callback */,
null /* no handler */
);
Bundle actual = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(actual);
return actual;
}
@Override
public Account getAccount() {
return CUSTOM_TOKEN_ACCOUNT;
}
};
validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE);
validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE);
}
/**
* Test successful getAuthToken() using a future with customTokens=false without
* expiring tokens.
*/
public void testGetAuthTokenWithFuture_Options_DefaultToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
OPTIONS_BUNDLE,
mActivity,
null /* no callback */,
null /* no handler */
);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
validateOptions(null, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsGetAuthToken);
validateSystemOptions(mockAuthenticator.mOptionsGetAuthToken);
}
/**
* Test successful getAuthToken() using a future with customTokens=false without
* expiring tokens.
*/
public void testGetAuthTokenWithFuture_Options_CustomToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
TokenFetcher fetcherWithOptions = new TokenFetcher() {
@Override
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(
getAccount(),
tokenType,
OPTIONS_BUNDLE,
false /* no failure notification */,
null /* no callback */,
null /* no handler */
);
Bundle actual = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(actual);
return actual;
}
@Override
public Account getAccount() {
return CUSTOM_TOKEN_ACCOUNT;
}
};
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
}
/**
* Test successful getAuthToken() using a future with customTokens=false without
* expiring tokens.
*/
public void testGetAuthTokenWithCallback_Options_Handler_DefaultToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null);
final HandlerThread handlerThread = new HandlerThread("accounts.test");
handlerThread.start();
TokenFetcher fetcherWithOptions = new TokenFetcher() {
@Override
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
final AtomicReference<Bundle> actualRef = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
actualRef.set(resultBundle);
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
} finally {
latch.countDown();
}
}
};
am.getAuthToken(getAccount(),
tokenType,
OPTIONS_BUNDLE,
false /* no failure notification */,
callback,
new Handler(handlerThread.getLooper()));
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
return actualRef.get();
}
@Override
public Account getAccount() {
return ACCOUNT;
}
};
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
}
/**
* Test successful getAuthToken() using a future with customTokens=false without
* expiring tokens.
*/
public void testGetAuthTokenWithCallback_Options_Handler_CustomToken_Success()
throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
final HandlerThread handlerThread = new HandlerThread("accounts.test");
handlerThread.start();
TokenFetcher fetcherWithOptions = new TokenFetcher() {
@Override
public Bundle fetch(String tokenType)
throws OperationCanceledException, AuthenticatorException, IOException {
final AtomicReference<Bundle> actualRef = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
actualRef.set(resultBundle);
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
} finally {
latch.countDown();
}
}
};
am.getAuthToken(getAccount(),
tokenType,
OPTIONS_BUNDLE,
false /* no failure notification */,
callback,
new Handler(handlerThread.getLooper()));
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
return actualRef.get();
}
@Override
public Account getAccount() {
return CUSTOM_TOKEN_ACCOUNT;
}
};
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
}
/**
* Test getAuthToken() with callback and handler
*/
public void testGetAuthTokenWithCallbackAndHandler() throws IOException, AuthenticatorException,
OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
testGetAuthTokenWithCallbackAndHandler(null /* handler */);
testGetAuthTokenWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testGetAuthTokenWithCallbackAndHandler(Handler handler) throws IOException,
AuthenticatorException, OperationCanceledException {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
}
finally {
latch.countDown();
}
}
};
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
false /* no failure notification */,
callback,
handler
);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* test getAuthToken() with options and callback and handler
*/
public void testGetAuthTokenWithOptionsAndCallback() throws IOException,
AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
testGetAuthTokenWithOptionsAndCallbackAndHandler(null /* handler */);
testGetAuthTokenWithOptionsAndCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testGetAuthTokenWithOptionsAndCallbackAndHandler(Handler handler) throws
IOException, AuthenticatorException, OperationCanceledException {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
}
finally {
latch.countDown();
}
}
};
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
OPTIONS_BUNDLE,
mActivity,
callback,
handler
);
Bundle resultBundle = futureBundle.getResult();
assertTrue(futureBundle.isDone());
assertNotNull(resultBundle);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test getAuthTokenByFeatures()
*/
public void testGetAuthTokenByFeatures() throws IOException, AuthenticatorException,
OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
Bundle resultBundle = getAuthTokenByFeature(
new String[] { NON_EXISTING_FEATURE },
null /* activity */
);
// Assert returned result
validateNullResult(resultBundle);
validateOptions(null, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
mockAuthenticator.clearData();
// Now test with existing features and an activity
resultBundle = getAuthTokenByFeature(
new String[] { NON_EXISTING_FEATURE },
mActivity
);
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
mockAuthenticator.clearData();
// Now test with existing features and no activity
resultBundle = getAuthTokenByFeature(
REQUIRED_FEATURES,
null /* activity */
);
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
validateOptions(null, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
mockAuthenticator.clearData();
// Now test with existing features and an activity
resultBundle = getAuthTokenByFeature(
REQUIRED_FEATURES,
mActivity
);
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
validateOptions(null, mockAuthenticator.mOptionsAddAccount);
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
}
/**
* Test confirmCredentials()
*/
public void testConfirmCredentials() throws IOException, AuthenticatorException,
OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT,
OPTIONS_BUNDLE,
mActivity,
null /* callback*/,
null /* handler */);
futureBundle.getResult();
// Assert returned result
validateCredentials();
}
/**
* Tests the setting of lastAuthenticatedTime on adding account
*/
public void testLastAuthenticatedTimeAfterAddAccount() throws IOException,
AuthenticatorException, OperationCanceledException {
assertTrue(addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD) > 0);
}
/**
* Test confirmCredentials() for account not on device. Just that no error
* should be thrown.
*/
public void testConfirmCredentialsAccountNotOnDevice() throws IOException,
AuthenticatorException, OperationCanceledException {
Account account = new Account("AccountNotOnThisDevice", ACCOUNT_TYPE);
AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(account,
OPTIONS_BUNDLE,
mActivity,
null /* callback */,
null /* handler */);
futureBundle.getResult();
}
/**
* Tests the setting of lastAuthenticatedTime on confirmCredentials being
* successful.
*/
public void testLastAuthenticatedTimeAfterConfirmCredentialsSuccess() throws IOException,
AuthenticatorException, OperationCanceledException {
long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
// Now this confirm credentials call returns true, which in turn
// should update the last authenticated timestamp.
Bundle result = am.confirmCredentials(ACCOUNT,
OPTIONS_BUNDLE, /* options */
null, /* activity */
null /* callback */,
null /* handler */).getResult();
long confirmedCredTime = result.getLong(
AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1);
assertTrue(confirmedCredTime > accountAddTime);
}
/**
* Tests the setting of lastAuthenticatedTime on updateCredentials being
* successful.
*/
public void testLastAuthenticatedTimeAfterUpdateCredentialsSuccess() throws IOException,
AuthenticatorException, OperationCanceledException {
long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
am.updateCredentials(ACCOUNT,
AUTH_TOKEN_TYPE,
OPTIONS_BUNDLE,
mActivity,
null /* callback */,
null /* handler */).getResult();
long updateCredTime = getLastAuthenticatedTime(ACCOUNT);
assertTrue(updateCredTime > accountAddTime);
}
/**
* Tests the setting of lastAuthenticatedTime on accountAuthenticated being
* successful.
*/
public void testLastAuthenticatedTimeAfterAccountAuthenticated() throws IOException,
AuthenticatorException, OperationCanceledException {
long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
mockAuthenticator.callAccountAuthenticated();
long accountAuthenticatedTime = getLastAuthenticatedTime(ACCOUNT);
assertTrue(accountAuthenticatedTime > accountAddTime);
}
/**
* Tests the setting of lastAuthenticatedTime on setPassword being
* successful.
*/
public void testLastAuthenticatedTimeAfterSetPassword() throws IOException,
AuthenticatorException, OperationCanceledException {
long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
mockAuthenticator.callSetPassword();
long setPasswordTime = getLastAuthenticatedTime(ACCOUNT);
assertTrue(setPasswordTime > accountAddTime);
}
/**
* Test confirmCredentials() with callback
*/
public void testConfirmCredentialsWithCallbackAndHandler() {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
testConfirmCredentialsWithCallbackAndHandler(null /* handler */);
testConfirmCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testConfirmCredentialsWithCallbackAndHandler(Handler handler) {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
// Assert returned result
validateCredentials();
assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
}
finally {
latch.countDown();
}
}
};
AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT,
OPTIONS_BUNDLE,
mActivity,
callback,
handler);
// Wait with timeout for the callback to do its work
try {
latch.await(3 * LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test updateCredentials()
*/
public void testUpdateCredentials() throws IOException, AuthenticatorException,
OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT,
AUTH_TOKEN_TYPE,
OPTIONS_BUNDLE,
mActivity,
null /* callback*/,
null /* handler */);
Bundle result = futureBundle.getResult();
validateAccountAndNoAuthTokenResult(result);
// Assert returned result
validateCredentials();
}
/**
* Test updateCredentials() with callback and handler
*/
public void testUpdateCredentialsWithCallbackAndHandler() throws IOException,
AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
testUpdateCredentialsWithCallbackAndHandler(null /* handler */);
testUpdateCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testUpdateCredentialsWithCallbackAndHandler(Handler handler) throws IOException,
AuthenticatorException, OperationCanceledException {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
// Assert returned result
validateCredentials();
assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
fail("should not throw an IOException");
} catch (AuthenticatorException e) {
fail("should not throw an AuthenticatorException");
}
finally {
latch.countDown();
}
}
};
AccountManagerFuture<Bundle> futureBundle = am.updateCredentials(ACCOUNT,
AUTH_TOKEN_TYPE,
OPTIONS_BUNDLE,
mActivity,
callback,
handler);
futureBundle.getResult();
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test editProperties()
*/
public void testEditProperties() throws IOException, AuthenticatorException,
OperationCanceledException {
AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE,
mActivity,
null /* callback */,
null /* handler*/);
Bundle result = futureBundle.getResult();
validateAccountAndNoAuthTokenResult(result);
// Assert returned result
assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType());
}
/**
* Test editProperties() with callback and handler
*/
public void testEditPropertiesWithCallbackAndHandler() {
testEditPropertiesWithCallbackAndHandler(null /* handler */);
testEditPropertiesWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
private void testEditPropertiesWithCallbackAndHandler(Handler handler) {
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
try {
// Assert returned result
assertEquals(ACCOUNT_TYPE, mockAuthenticator.getAccountType());
}
finally {
latch.countDown();
}
}
};
AccountManagerFuture<Bundle> futureBundle = am.editProperties(ACCOUNT_TYPE,
mActivity,
callback,
handler);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test addOnAccountsUpdatedListener() with handler
*/
public void testAddOnAccountsUpdatedListenerWithHandler() throws IOException,
AuthenticatorException, OperationCanceledException {
testAddOnAccountsUpdatedListenerWithHandler(null /* handler */,
false /* updateImmediately */);
// Need to cleanup intermediate state
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(null /* handler */,
true /* updateImmediately */);
// Need to cleanup intermediate state
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
false /* updateImmediately */);
// Need to cleanup intermediate state
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
true /* updateImmediately */);
}
private void testAddOnAccountsUpdatedListenerWithHandler(Handler handler,
boolean updateImmediately) {
final CountDownLatch latch = new CountDownLatch(1);
OnAccountsUpdateListener listener = new OnAccountsUpdateListener() {
@Override
public void onAccountsUpdated(Account[] accounts) {
latch.countDown();
}
};
// Add a listener
am.addOnAccountsUpdatedListener(listener,
handler,
updateImmediately);
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
// Cleanup
am.removeOnAccountsUpdatedListener(listener);
}
/**
* Test removeOnAccountsUpdatedListener() with handler
*/
public void testRemoveOnAccountsUpdatedListener() throws IOException, AuthenticatorException,
OperationCanceledException {
testRemoveOnAccountsUpdatedListenerWithHandler(null /* handler */);
// Need to cleanup intermediate state
assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
testRemoveOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()));
}
private void testRemoveOnAccountsUpdatedListenerWithHandler(Handler handler) {
final CountDownLatch latch = new CountDownLatch(1);
OnAccountsUpdateListener listener = new OnAccountsUpdateListener() {
@Override
public void onAccountsUpdated(Account[] accounts) {
fail("should not be called");
}
};
// First add a listener
am.addOnAccountsUpdatedListener(listener,
handler,
false /* updateImmediately */);
// Then remove the listener
am.removeOnAccountsUpdatedListener(listener);
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
/**
* Test hasFeature
*/
public void testHasFeature()
throws IOException, AuthenticatorException, OperationCanceledException {
assertHasFeature(null /* handler */);
assertHasFeature(new Handler(Looper.getMainLooper()));
assertHasFeatureWithCallback(null /* handler */);
assertHasFeatureWithCallback(new Handler(Looper.getMainLooper()));
}
private void assertHasFeature(Handler handler)
throws IOException, AuthenticatorException, OperationCanceledException {
Bundle resultBundle = addAccount(am,
ACCOUNT_TYPE,
AUTH_TOKEN_TYPE,
REQUIRED_FEATURES,
OPTIONS_BUNDLE,
mActivity,
null /* callback */,
null /* handler */);
// Assert parameters has been passed correctly
validateAccountAndAuthTokenType();
validateFeatures();
AccountManagerFuture<Boolean> booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{FEATURE_1},
null /* callback */,
handler);
assertTrue(booleanFuture.getResult());
booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{FEATURE_2},
null /* callback */,
handler);
assertTrue(booleanFuture.getResult());
booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{FEATURE_1, FEATURE_2},
null /* callback */,
handler);
assertTrue(booleanFuture.getResult());
booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE},
null /* callback */,
handler);
assertFalse(booleanFuture.getResult());
booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE, FEATURE_1},
null /* callback */,
handler);
assertFalse(booleanFuture.getResult());
booleanFuture = am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2},
null /* callback */,
handler);
assertFalse(booleanFuture.getResult());
}
private AccountManagerCallback<Boolean> getAssertTrueCallback(final CountDownLatch latch) {
return new AccountManagerCallback<Boolean>() {
@Override
public void run(AccountManagerFuture<Boolean> booleanFuture) {
try {
// Assert returned result should be TRUE
assertTrue(booleanFuture.getResult());
} catch (Exception e) {
fail("Exception: " + e);
} finally {
latch.countDown();
}
}
};
}
private AccountManagerCallback<Boolean> getAssertFalseCallback(final CountDownLatch latch) {
return new AccountManagerCallback<Boolean>() {
@Override
public void run(AccountManagerFuture<Boolean> booleanFuture) {
try {
// Assert returned result should be FALSE
assertFalse(booleanFuture.getResult());
} catch (Exception e) {
fail("Exception: " + e);
} finally {
latch.countDown();
}
}
};
}
private void waitForLatch(final CountDownLatch latch) {
// Wait with timeout for the callback to do its work
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("should not throw an InterruptedException");
}
}
private void assertHasFeatureWithCallback(Handler handler)
throws IOException, AuthenticatorException, OperationCanceledException {
Bundle resultBundle = addAccount(am,
ACCOUNT_TYPE,
AUTH_TOKEN_TYPE,
REQUIRED_FEATURES,
OPTIONS_BUNDLE,
mActivity,
null /* callback */,
null /* handler */);
// Assert parameters has been passed correctly
validateAccountAndAuthTokenType();
validateFeatures();
CountDownLatch latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{FEATURE_1},
getAssertTrueCallback(latch),
handler);
waitForLatch(latch);
latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{FEATURE_2},
getAssertTrueCallback(latch),
handler);
waitForLatch(latch);
latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{FEATURE_1, FEATURE_2},
getAssertTrueCallback(latch),
handler);
waitForLatch(latch);
latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE},
getAssertFalseCallback(latch),
handler);
waitForLatch(latch);
latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE, FEATURE_1},
getAssertFalseCallback(latch),
handler);
waitForLatch(latch);
latch = new CountDownLatch(1);
am.hasFeatures(ACCOUNT,
new String[]{NON_EXISTING_FEATURE, FEATURE_1, FEATURE_2},
getAssertFalseCallback(latch),
handler);
waitForLatch(latch);
}
private long getLastAuthenticatedTime(Account account) throws OperationCanceledException,
AuthenticatorException, IOException {
Bundle options = new Bundle();
options.putBoolean(MockAccountAuthenticator.KEY_RETURN_INTENT, true);
// Not really confirming, but a way to get last authenticated timestamp
Bundle result = am.confirmCredentials(account,
options,// OPTIONS_BUNDLE,
null, /* activity */
null /* callback */,
null /* handler */).getResult();
return result.getLong(
AccountManager.KEY_LAST_AUTHENTICATED_TIME, -1);
}
private long addAccountAndReturnAccountAddedTime(Account account, String password)
throws OperationCanceledException, AuthenticatorException, IOException {
addAccountExplicitly(account, password, null /* userData */);
return getLastAuthenticatedTime(account);
}
/**
* Tests that AccountManagerService is properly caching data.
*/
public void testGetsAreCached() {
// Add an account,
assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT));
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
// Then verify that we don't hit disk retrieving it,
StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build());
Account[] accounts = am.getAccounts();
assertNotNull(accounts);
assertTrue(accounts.length > 0);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
}