Add cache for Account Visibility values.
Test: cts Bug: 36485175 Change-Id: I075b81f8b287199d51ea09dc1108748310ab846c
This commit is contained in:
@@ -202,16 +202,17 @@ public class AccountManagerService
|
||||
new HashMap<Account, Integer>();
|
||||
final Object cacheLock = new Object();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
final HashMap<String, Account[]> accountCache =
|
||||
new LinkedHashMap<>();
|
||||
final HashMap<String, Account[]> accountCache = new LinkedHashMap<>();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final TokenCache accountTokenCaches = new TokenCache();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final Map<Account, Map<String, Integer>> visibilityCache = new HashMap<>();
|
||||
|
||||
/** protected by the {@link #mReceiversForType}
|
||||
/** protected by the {@link #mReceiversForType},
|
||||
* type -> (packageName -> number of active receivers)
|
||||
* type == null is used to get notifications about all account types
|
||||
*/
|
||||
@@ -524,25 +525,29 @@ public class AccountManagerService
|
||||
String.format("uid %s cannot get secrets for account %s", callingUid, account);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
return getPackagesAndVisibilityForAccount(account, accounts);
|
||||
synchronized (accounts.cacheLock) {
|
||||
return getPackagesAndVisibilityForAccountLocked(account, accounts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all package names and visibility values, which were set for given account.
|
||||
* Returns Map with all package names and visibility values for given account.
|
||||
* The method and returned map must be guarded by accounts.cacheLock
|
||||
*
|
||||
* @param account Account to get visibility values.
|
||||
* @param accounts UserAccount that currently hosts the account and application
|
||||
*
|
||||
* @return Map from package names to visibility.
|
||||
* @return Map with cache for package names to visibility.
|
||||
*/
|
||||
private Map<String, Integer> getPackagesAndVisibilityForAccount(Account account,
|
||||
private @NonNull Map<String, Integer> getPackagesAndVisibilityForAccountLocked(Account account,
|
||||
UserAccounts accounts) {
|
||||
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
|
||||
try {
|
||||
return accounts.accountsDb.findAllVisibilityValuesForAccount(account);
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(oldPolicy);
|
||||
Map<String, Integer> accountVisibility = accounts.visibilityCache.get(account);
|
||||
if (accountVisibility == null) {
|
||||
Log.d(TAG, "Visibility was not initialized");
|
||||
accountVisibility = new HashMap<>();
|
||||
accounts.visibilityCache.put(account, accountVisibility);
|
||||
}
|
||||
return accountVisibility;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -572,14 +577,13 @@ public class AccountManagerService
|
||||
* @return Visibility value, AccountManager.VISIBILITY_UNDEFINED if no value was stored.
|
||||
*
|
||||
*/
|
||||
private int getAccountVisibility(Account account, String packageName, UserAccounts accounts) {
|
||||
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
|
||||
try {
|
||||
Integer visibility =
|
||||
accounts.accountsDb.findAccountVisibility(account, packageName);
|
||||
private int getAccountVisibilityFromCache(Account account, String packageName,
|
||||
UserAccounts accounts) {
|
||||
synchronized (accounts.cacheLock) {
|
||||
Map<String, Integer> accountVisibility =
|
||||
getPackagesAndVisibilityForAccountLocked(account, accounts);
|
||||
Integer visibility = accountVisibility.get(packageName);
|
||||
return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(oldPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,9 +599,7 @@ public class AccountManagerService
|
||||
*/
|
||||
private Integer resolveAccountVisibility(Account account, @NonNull String packageName,
|
||||
UserAccounts accounts) {
|
||||
|
||||
Preconditions.checkNotNull(packageName, "packageName cannot be null");
|
||||
|
||||
int uid = -1;
|
||||
try {
|
||||
long identityToken = clearCallingIdentity();
|
||||
@@ -630,7 +632,7 @@ public class AccountManagerService
|
||||
}
|
||||
|
||||
// Return stored value if it was set.
|
||||
int visibility = getAccountVisibility(account, packageName, accounts);
|
||||
int visibility = getAccountVisibilityFromCache(account, packageName, accounts);
|
||||
|
||||
if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
|
||||
return visibility;
|
||||
@@ -652,13 +654,13 @@ public class AccountManagerService
|
||||
|| canReadContacts || isPrivileged) {
|
||||
// Use legacy for preO apps with GET_ACCOUNTS permission or pre/postO with signature
|
||||
// match.
|
||||
visibility = getAccountVisibility(account,
|
||||
visibility = getAccountVisibilityFromCache(account,
|
||||
AccountManager.PACKAGE_NAME_KEY_LEGACY_VISIBLE, accounts);
|
||||
if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
|
||||
visibility = AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
|
||||
}
|
||||
} else {
|
||||
visibility = getAccountVisibility(account,
|
||||
visibility = getAccountVisibilityFromCache(account,
|
||||
AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, accounts);
|
||||
if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
|
||||
visibility = AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
|
||||
@@ -751,21 +753,10 @@ public class AccountManagerService
|
||||
packagesToVisibility = new HashMap<>();
|
||||
}
|
||||
|
||||
final long accountId = accounts.accountsDb.findDeAccountId(account);
|
||||
if (accountId < 0) {
|
||||
if (!updateAccountVisibilityLocked(account, packageName, newVisibility, accounts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
|
||||
try {
|
||||
if (!accounts.accountsDb.setAccountVisibility(accountId, packageName,
|
||||
newVisibility)) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(oldPolicy);
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
|
||||
if (packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE) {
|
||||
@@ -778,6 +769,29 @@ public class AccountManagerService
|
||||
}
|
||||
}
|
||||
|
||||
// Update account visibility in cache and database.
|
||||
private boolean updateAccountVisibilityLocked(Account account, String packageName,
|
||||
int newVisibility, UserAccounts accounts) {
|
||||
final long accountId = accounts.accountsDb.findDeAccountId(account);
|
||||
if (accountId < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
|
||||
try {
|
||||
if (!accounts.accountsDb.setAccountVisibility(accountId, packageName,
|
||||
newVisibility)) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(oldPolicy);
|
||||
}
|
||||
Map<String, Integer> accountVisibility =
|
||||
getPackagesAndVisibilityForAccountLocked(account, accounts);
|
||||
accountVisibility.put(packageName, newVisibility);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAccountListener(String[] accountTypes, String opPackageName) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
@@ -1042,9 +1056,12 @@ public class AccountManagerService
|
||||
accounts.userDataCache.remove(account);
|
||||
accounts.authTokenCache.remove(account);
|
||||
accounts.accountTokenCaches.remove(account);
|
||||
accounts.visibilityCache.remove(account);
|
||||
|
||||
for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
|
||||
if (packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE) {
|
||||
for (Entry<String, Integer> packageToVisibility :
|
||||
packagesToVisibility.entrySet()) {
|
||||
if (packageToVisibility.getValue()
|
||||
!= AccountManager.VISIBILITY_NOT_VISIBLE) {
|
||||
notifyPackage(packageToVisibility.getKey(), accounts);
|
||||
}
|
||||
}
|
||||
@@ -1067,6 +1084,7 @@ public class AccountManagerService
|
||||
}
|
||||
accounts.accountCache.put(accountType, accountsForType);
|
||||
}
|
||||
accounts.visibilityCache.putAll(accountsDb.findAllVisibilityValues());
|
||||
} finally {
|
||||
if (accountDeleted) {
|
||||
sendAccountsChangedBroadcast(accounts.userId);
|
||||
@@ -1181,19 +1199,31 @@ public class AccountManagerService
|
||||
}
|
||||
|
||||
private void removeVisibilityValuesForPackage(String packageName) {
|
||||
if (isSpecialPackageKey(packageName)) {
|
||||
return;
|
||||
}
|
||||
synchronized (mUsers) {
|
||||
for (int i = 0; i < mUsers.size(); i++) {
|
||||
UserAccounts accounts = mUsers.valueAt(i);
|
||||
try {
|
||||
int uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
|
||||
} catch (NameNotFoundException e) {
|
||||
// package does not exist - remove visibility values
|
||||
accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
|
||||
int numberOfUsers = mUsers.size();
|
||||
for (int i = 0; i < numberOfUsers; i++) {
|
||||
UserAccounts accounts = mUsers.valueAt(i);
|
||||
try {
|
||||
mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
|
||||
} catch (NameNotFoundException e) {
|
||||
// package does not exist - remove visibility values
|
||||
accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
|
||||
synchronized(accounts.cacheLock) {
|
||||
for (Account account : accounts.visibilityCache.keySet()) {
|
||||
Map<String, Integer> accountVisibility =
|
||||
getPackagesAndVisibilityForAccountLocked(account, accounts);
|
||||
accountVisibility.remove(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onCleanupUser(int userId) {
|
||||
Log.i(TAG, "onCleanupUser " + userId);
|
||||
UserAccounts accounts;
|
||||
@@ -1849,6 +1879,7 @@ public class AccountManagerService
|
||||
*/
|
||||
Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
|
||||
Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
|
||||
Map<String, Integer> tmpVisibility = accounts.visibilityCache.get(accountToRename);
|
||||
removeAccountFromCacheLocked(accounts, accountToRename);
|
||||
/*
|
||||
* Update the cached data associated with the renamed
|
||||
@@ -1856,6 +1887,7 @@ public class AccountManagerService
|
||||
*/
|
||||
accounts.userDataCache.put(renamedAccount, tmpData);
|
||||
accounts.authTokenCache.put(renamedAccount, tmpTokens);
|
||||
accounts.visibilityCache.put(renamedAccount, tmpVisibility);
|
||||
accounts.previousNameCache.put(
|
||||
renamedAccount,
|
||||
new AtomicReference<>(accountToRename.name));
|
||||
@@ -5369,6 +5401,7 @@ public class AccountManagerService
|
||||
accounts.userDataCache.remove(account);
|
||||
accounts.authTokenCache.remove(account);
|
||||
accounts.previousNameCache.remove(account);
|
||||
accounts.visibilityCache.remove(account);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -909,7 +909,7 @@ class AccountsDb implements AutoCloseable {
|
||||
}
|
||||
|
||||
Integer findAccountVisibility(Account account, String packageName) {
|
||||
SQLiteDatabase db = mDeDatabase.getWritableDatabase();
|
||||
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
|
||||
final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE},
|
||||
SELECTION_ACCOUNTS_ID_BY_ACCOUNT + " AND " + VISIBILITY_PACKAGE + "=? ",
|
||||
new String[] {account.name, account.type, packageName}, null, null, null);
|
||||
@@ -924,7 +924,7 @@ class AccountsDb implements AutoCloseable {
|
||||
}
|
||||
|
||||
Integer findAccountVisibility(long accountId, String packageName) {
|
||||
SQLiteDatabase db = mDeDatabase.getWritableDatabase();
|
||||
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
|
||||
final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE},
|
||||
VISIBILITY_ACCOUNTS_ID + "=? AND " + VISIBILITY_PACKAGE + "=? ",
|
||||
new String[] {String.valueOf(accountId), packageName}, null, null, null);
|
||||
@@ -972,6 +972,41 @@ class AccountsDb implements AutoCloseable {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map account -> (package -> visibility)
|
||||
*/
|
||||
Map <Account, Map<String, Integer>> findAllVisibilityValues() {
|
||||
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
|
||||
Map<Account, Map<String, Integer>> result = new HashMap<>();
|
||||
Cursor cursor = db.rawQuery(
|
||||
"SELECT " + TABLE_VISIBILITY + "." + VISIBILITY_PACKAGE
|
||||
+ ", " + TABLE_VISIBILITY + "." + VISIBILITY_VALUE
|
||||
+ ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
|
||||
+ ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE
|
||||
+ " FROM " + TABLE_VISIBILITY
|
||||
+ " JOIN " + TABLE_ACCOUNTS
|
||||
+ " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
|
||||
+ " = " + TABLE_VISIBILITY + "." + VISIBILITY_ACCOUNTS_ID, null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
String packageName = cursor.getString(0);
|
||||
Integer visibility = cursor.getInt(1);
|
||||
String accountName = cursor.getString(2);
|
||||
String accountType = cursor.getString(3);
|
||||
Account account = new Account(accountName, accountType);
|
||||
Map <String, Integer> accountVisibility = result.get(account);
|
||||
if (accountVisibility == null) {
|
||||
accountVisibility = new HashMap<>();
|
||||
result.put(account, accountVisibility);
|
||||
}
|
||||
accountVisibility.put(packageName, visibility);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean deleteAccountVisibilityForPackage(String packageName) {
|
||||
SQLiteDatabase db = mDeDatabase.getWritableDatabase();
|
||||
return db.delete(TABLE_VISIBILITY, VISIBILITY_PACKAGE + "=? ",
|
||||
|
||||
@@ -94,7 +94,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
* <p>Run with:<pre>
|
||||
* mmma -j40 frameworks/base/services/tests/servicestests
|
||||
* adb install -r ${OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
|
||||
* adb shell am instrument -w -e class package com.android.server.accounts \
|
||||
* adb shell am instrument -w -e package com.android.server.accounts \
|
||||
* com.android.frameworks.servicestests\
|
||||
* /android.support.test.runner.AndroidJUnitRunner
|
||||
* </pre>
|
||||
|
||||
@@ -384,6 +384,42 @@ public class AccountsDbTest {
|
||||
assertFalse(mAccountsDb.deleteAccountVisibilityForPackage(packageName1)); // 2nd attempt.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAllVisibilityValues() {
|
||||
long accId = 10;
|
||||
long accId2 = 11;
|
||||
String packageName1 = "com.example.one";
|
||||
String packageName2 = "com.example.two";
|
||||
Account account = new Account("name", "example.com");
|
||||
Account account2 = new Account("name2", "example2.com");
|
||||
assertNull(mAccountsDb.findAccountVisibility(account, packageName1));
|
||||
|
||||
mAccountsDb.insertDeAccount(account, accId);
|
||||
assertNull(mAccountsDb.findAccountVisibility(account, packageName1));
|
||||
assertNull(mAccountsDb.findAccountVisibility(accId, packageName1));
|
||||
mAccountsDb.insertDeAccount(account2, accId2);
|
||||
|
||||
mAccountsDb.setAccountVisibility(accId, packageName1, 1);
|
||||
mAccountsDb.setAccountVisibility(accId, packageName2, 2);
|
||||
mAccountsDb.setAccountVisibility(accId2, packageName1, 1);
|
||||
|
||||
Map<Account, Map<String, Integer>> vis = mAccountsDb.findAllVisibilityValues();
|
||||
assertEquals(vis.size(), 2);
|
||||
Map<String, Integer> accnt1Visibility = vis.get(account);
|
||||
assertEquals(accnt1Visibility.size(), 2);
|
||||
assertEquals(accnt1Visibility.get(packageName1), Integer.valueOf(1));
|
||||
assertEquals(accnt1Visibility.get(packageName2), Integer.valueOf(2));
|
||||
Map<String, Integer> accnt2Visibility = vis.get(account2);
|
||||
assertEquals(accnt2Visibility.size(), 1);
|
||||
assertEquals(accnt2Visibility.get(packageName1), Integer.valueOf(1));
|
||||
|
||||
mAccountsDb.setAccountVisibility(accId2, packageName2, 3);
|
||||
vis = mAccountsDb.findAllVisibilityValues();
|
||||
accnt2Visibility = vis.get(account2);
|
||||
assertEquals(accnt2Visibility.size(), 2);
|
||||
assertEquals(accnt2Visibility.get(packageName2), Integer.valueOf(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibilityCleanupTrigger() {
|
||||
long accId = 10;
|
||||
|
||||
Reference in New Issue
Block a user