Merge "Implement Account Discovery API."
This commit is contained in:
committed by
Android (Google) Code Review
commit
7b5bbb5509
@@ -53,9 +53,12 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.SuppressWarnings;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -864,11 +867,17 @@ public class AccountManager {
|
||||
*
|
||||
* @param account The account for which visibility data should be returned.
|
||||
*
|
||||
* @return Map from uid to visibility for given account
|
||||
* @return Map from uid to visibility for given account.
|
||||
*/
|
||||
public Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account) {
|
||||
// TODO implement.
|
||||
return null;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Integer, Integer> result = (Map<Integer, Integer>) mService
|
||||
.getUidsAndVisibilityForAccount(account);
|
||||
return result;
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2110,10 +2119,23 @@ public class AccountManager {
|
||||
synchronized (mAccountsUpdatedListeners) {
|
||||
try {
|
||||
if (mAccountsUpdatedListeners.containsKey(listener)) {
|
||||
listener.onAccountsUpdated(accountsCopy);
|
||||
Set<String> types = mAccountsUpdatedListenersTypes.get(listener);
|
||||
if (types != null) {
|
||||
// filter by account type;
|
||||
ArrayList<Account> filtered = new ArrayList<>();
|
||||
for (Account account : accountsCopy) {
|
||||
if (types.contains(account.type)) {
|
||||
filtered.add(account);
|
||||
}
|
||||
}
|
||||
listener.onAccountsUpdated(
|
||||
filtered.toArray(new Account[filtered.size()]));
|
||||
} else {
|
||||
listener.onAccountsUpdated(accountsCopy);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
// Better luck next time. If the problem was disk-full,
|
||||
// Better luck next time. If the problem was disk-full,
|
||||
// the STORAGE_OK intent will re-trigger the update.
|
||||
Log.e(TAG, "Can't update accounts", e);
|
||||
}
|
||||
@@ -2759,6 +2781,9 @@ public class AccountManager {
|
||||
private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
|
||||
Maps.newHashMap();
|
||||
|
||||
private final HashMap<OnAccountsUpdateListener, Set<String> > mAccountsUpdatedListenersTypes =
|
||||
Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
|
||||
* so that it can read the updated list of accounts and send them to the listener
|
||||
@@ -2784,7 +2809,7 @@ public class AccountManager {
|
||||
* accounts of any type related to the caller. This method is equivalent to
|
||||
* addOnAccountsUpdatedListener(listener, handler, updateImmediately, null)
|
||||
*
|
||||
* @see #addOnAccountsUpdatedListener(OnAccountsUpdateListener, Handler, boolean, Handler,
|
||||
* @see #addOnAccountsUpdatedListener(OnAccountsUpdateListener, Handler, boolean,
|
||||
* String[])
|
||||
*/
|
||||
public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
|
||||
@@ -2828,7 +2853,10 @@ public class AccountManager {
|
||||
final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
|
||||
|
||||
mAccountsUpdatedListeners.put(listener, handler);
|
||||
|
||||
if (accountTypes != null) {
|
||||
mAccountsUpdatedListenersTypes.put(listener,
|
||||
new HashSet<String>(Arrays.asList(accountTypes)));
|
||||
}
|
||||
|
||||
if (wasEmpty) {
|
||||
// Register a broadcast receiver to monitor account changes
|
||||
@@ -2870,6 +2898,7 @@ public class AccountManager {
|
||||
return;
|
||||
}
|
||||
mAccountsUpdatedListeners.remove(listener);
|
||||
mAccountsUpdatedListenersTypes.remove(listener);
|
||||
if (mAccountsUpdatedListeners.isEmpty()) {
|
||||
mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ public class ChooseAccountActivity extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// TODO This activity is only used by getAuthTokenByFeatures and can not see
|
||||
// VISIBILITY_USER_MANAGED_NOT_VISIBLE accounts. It should be moved to account managed
|
||||
// service.
|
||||
mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS);
|
||||
mAccountManagerResponse =
|
||||
getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE);
|
||||
|
||||
@@ -40,7 +40,9 @@ import com.android.internal.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -110,7 +112,7 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
|
||||
private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName";
|
||||
private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount";
|
||||
private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountList";
|
||||
private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountAndVisibilityList";
|
||||
|
||||
private static final int SELECTED_ITEM_NONE = -1;
|
||||
|
||||
@@ -120,7 +122,11 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
private boolean mSelectedAddNewAccount = false;
|
||||
private String mDescriptionOverride;
|
||||
|
||||
private ArrayList<Account> mAccounts;
|
||||
private Map<Account, Integer> mAccounts;
|
||||
// TODO Redesign flow to show NOT_VISIBLE accounts
|
||||
// and display a warning if they are selected.
|
||||
// Currently NOT_VISBILE accounts are not shown at all.
|
||||
private ArrayList<Account> mPossiblyVisibleAccounts;
|
||||
private int mPendingRequest = REQUEST_NULL;
|
||||
private Parcelable[] mExistingAccounts = null;
|
||||
private int mSelectedItemIndex;
|
||||
@@ -164,12 +170,12 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
|
||||
|
||||
// Makes sure that any user selection is preserved across orientation changes.
|
||||
mSelectedAccountName = savedInstanceState.getString(
|
||||
KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
|
||||
|
||||
mSelectedAddNewAccount = savedInstanceState.getBoolean(
|
||||
KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
|
||||
mAccounts = savedInstanceState.getParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST);
|
||||
mSelectedAccountName =
|
||||
savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
|
||||
mSelectedAddNewAccount =
|
||||
savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
|
||||
mAccounts = (Map<Account, Integer>) savedInstanceState
|
||||
.getSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST);
|
||||
} else {
|
||||
mPendingRequest = REQUEST_NULL;
|
||||
mExistingAccounts = null;
|
||||
@@ -220,9 +226,15 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
}
|
||||
}
|
||||
|
||||
String[] listItems = getListOfDisplayableOptions(mAccounts);
|
||||
mSelectedItemIndex = getItemIndexToSelect(
|
||||
mAccounts, mSelectedAccountName, mSelectedAddNewAccount);
|
||||
mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
|
||||
for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
|
||||
if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
|
||||
mPossiblyVisibleAccounts.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts);
|
||||
mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName,
|
||||
mSelectedAddNewAccount);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.choose_type_and_account);
|
||||
@@ -250,15 +262,18 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
|
||||
}
|
||||
if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
|
||||
if (mSelectedItemIndex == mAccounts.size()) {
|
||||
if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
|
||||
outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);
|
||||
} else {
|
||||
outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
|
||||
outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,
|
||||
mAccounts.get(mSelectedItemIndex).name);
|
||||
mPossiblyVisibleAccounts.get(mSelectedItemIndex).name);
|
||||
}
|
||||
}
|
||||
outState.putParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST, mAccounts);
|
||||
// should be HashMap by default.
|
||||
HashMap<Account, Integer> accountsHashMap = (mAccounts instanceof HashMap)
|
||||
? (HashMap) mAccounts : new HashMap<Account, Integer>(mAccounts);
|
||||
outState.putSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST, accountsHashMap);
|
||||
}
|
||||
|
||||
public void onCancelButtonClicked(View view) {
|
||||
@@ -266,11 +281,11 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
}
|
||||
|
||||
public void onOkButtonClicked(View view) {
|
||||
if (mSelectedItemIndex == mAccounts.size()) {
|
||||
if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
|
||||
// Selected "Add New Account" option
|
||||
startChooseAccountTypeActivity();
|
||||
} else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
|
||||
onAccountSelected(mAccounts.get(mSelectedItemIndex));
|
||||
onAccountSelected(mPossiblyVisibleAccounts.get(mSelectedItemIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,6 +336,7 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
}
|
||||
|
||||
if (accountName == null || accountType == null) {
|
||||
// new account was added.
|
||||
Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
|
||||
mCallingPackage, mCallingUid);
|
||||
Set<Account> preExistingAccounts = new HashSet<Account>();
|
||||
@@ -328,6 +344,7 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
preExistingAccounts.add((Account) accountParcel);
|
||||
}
|
||||
for (Account account : currentAccounts) {
|
||||
// New account is visible to the app - return it.
|
||||
if (!preExistingAccounts.contains(account)) {
|
||||
accountName = account.name;
|
||||
accountType = account.type;
|
||||
@@ -409,13 +426,27 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
}
|
||||
|
||||
private void setResultAndFinish(final String accountName, final String accountType) {
|
||||
// Mark account as visible since user chose it.
|
||||
Account account = new Account(accountName, accountType);
|
||||
Integer oldVisibility = mAccounts.get(account);
|
||||
// oldVisibility is null if new account was added
|
||||
if (oldVisibility == null) {
|
||||
Map<Account, Integer> accountsAndVisibility = AccountManager.get(this)
|
||||
.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
|
||||
oldVisibility = accountsAndVisibility.get(account);
|
||||
}
|
||||
if (oldVisibility != null
|
||||
&& oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) {
|
||||
AccountManager.get(this).setAccountVisibility(account, mCallingUid,
|
||||
AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
|
||||
}
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
|
||||
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
|
||||
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
|
||||
+ "selected account " + accountName + ", " + accountType);
|
||||
Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account "
|
||||
+ accountName + ", " + accountType);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
@@ -474,25 +505,28 @@ public class ChooseTypeAndAccountActivity extends Activity
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of Account objects for each account that is acceptable. Filter out
|
||||
* accounts that don't match the allowable types, if provided, or that don't match the
|
||||
* allowable accounts, if provided.
|
||||
* Create a list of Account objects for each account that is acceptable. Filter out accounts
|
||||
* that don't match the allowable types, if provided, or that don't match the allowable
|
||||
* accounts, if provided.
|
||||
*/
|
||||
private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
|
||||
final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
|
||||
mCallingUid);
|
||||
ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
|
||||
for (Account account : accounts) {
|
||||
if (mSetOfAllowableAccounts != null && !mSetOfAllowableAccounts.contains(account)) {
|
||||
continue;
|
||||
}
|
||||
if (mSetOfRelevantAccountTypes != null
|
||||
&& !mSetOfRelevantAccountTypes.contains(account.type)) {
|
||||
continue;
|
||||
}
|
||||
accountsToPopulate.add(account);
|
||||
}
|
||||
return accountsToPopulate;
|
||||
private Map<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
|
||||
Map<Account, Integer> accountsAndVisibility =
|
||||
accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
|
||||
|
||||
Map<Account, Integer> accountsToPopulate =
|
||||
new HashMap<Account, Integer>(accountsAndVisibility.size());
|
||||
for (Map.Entry<Account, Integer> entry : accountsAndVisibility.entrySet()) {
|
||||
if (mSetOfAllowableAccounts != null
|
||||
&& !mSetOfAllowableAccounts.contains(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
if (mSetOfRelevantAccountTypes != null
|
||||
&& !mSetOfRelevantAccountTypes.contains(entry.getKey().type)) {
|
||||
continue;
|
||||
}
|
||||
accountsToPopulate.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return accountsToPopulate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,8 +108,8 @@ interface IAccountManager {
|
||||
void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
|
||||
String statusToken);
|
||||
|
||||
/* Allows Authenticator to get UIDs of packages which registered to receive updates about given account type.*/
|
||||
int[] getRequestingUidsForType(String accountType);
|
||||
/* Returns Map<Integer, Integer> from UID to visibility with all values stored for given account*/
|
||||
Map getUidsAndVisibilityForAccount(in Account account);
|
||||
|
||||
boolean addAccountExplicitlyWithVisibility(in Account account, String password, in Bundle extras,
|
||||
in Map visibility);
|
||||
@@ -117,7 +117,7 @@ interface IAccountManager {
|
||||
boolean setAccountVisibility(in Account a, int uid, int newVisibility);
|
||||
int getAccountVisibility(in Account a, int uid);
|
||||
|
||||
/* Type may be null returns Map <Account, Integer>*/
|
||||
/* Type may be null returns Map <Account, Integer>*/
|
||||
Map getAccountsAndVisibilityForPackage(in String packageName, in String accountType);
|
||||
|
||||
/* Check if the package in a user can access an account */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -355,7 +355,7 @@ class AccountsDb implements AutoCloseable {
|
||||
boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) {
|
||||
SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
|
||||
return db.delete(CE_TABLE_AUTHTOKENS,
|
||||
AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
|
||||
AUTHTOKENS_ACCOUNTS_ID + "=?" + " AND " + AUTHTOKENS_TYPE + "=?",
|
||||
new String[]{String.valueOf(accountId), authtokenType}) > 0;
|
||||
}
|
||||
|
||||
@@ -946,12 +946,13 @@ class AccountsDb implements AutoCloseable {
|
||||
/**
|
||||
* Returns a map from uid to visibility value.
|
||||
*/
|
||||
Map<Integer, Integer> findAccountVisibilityForAccountId(long accountId) {
|
||||
Map<Integer, Integer> findAllVisibilityValuesForAccount(Account account) {
|
||||
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
final Cursor cursor = db.query(TABLE_VISIBILITY,
|
||||
new String[] {VISIBILITY_UID, VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? ",
|
||||
new String[] {String.valueOf(accountId)}, null, null, null);
|
||||
final Cursor cursor =
|
||||
db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_UID, VISIBILITY_VALUE},
|
||||
SELECTION_ACCOUNTS_ID_BY_ACCOUNT,
|
||||
new String[] {account.name, account.type}, null, null, null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
result.put(cursor.getInt(0), cursor.getInt(1));
|
||||
@@ -1306,4 +1307,4 @@ class AccountsDb implements AutoCloseable {
|
||||
return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1262,25 +1262,6 @@ public class AccountManagerServiceTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testHasFeaturesReadAccountsNotPermitted() throws Exception {
|
||||
unlockSystemUser();
|
||||
when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
|
||||
PackageManager.PERMISSION_DENIED);
|
||||
when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
|
||||
.thenReturn(PackageManager.SIGNATURE_NO_MATCH);
|
||||
try {
|
||||
mAms.hasFeatures(
|
||||
mMockAccountManagerResponse, // response
|
||||
AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
|
||||
new String[] {"feature1", "feature2"}, // features
|
||||
"testPackage"); // opPackageName
|
||||
fail("SecurityException expected. But no exception was thrown.");
|
||||
} catch (SecurityException e) {
|
||||
// SecurityException is expected.
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testHasFeaturesReturnNullResult() throws Exception {
|
||||
unlockSystemUser();
|
||||
|
||||
@@ -374,7 +374,7 @@ public class AccountsDbTest {
|
||||
mAccountsDb.setAccountVisibility(accId, uid2, 3);
|
||||
assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(3));
|
||||
|
||||
Map<Integer, Integer> vis = mAccountsDb.findAccountVisibilityForAccountId(accId);
|
||||
Map<Integer, Integer> vis = mAccountsDb.findAllVisibilityValuesForAccount(account);
|
||||
assertEquals(vis.size(), 2);
|
||||
assertEquals(vis.get(uid1), Integer.valueOf(1));
|
||||
assertEquals(vis.get(uid2), Integer.valueOf(3));
|
||||
|
||||
Reference in New Issue
Block a user