Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71fa5261 authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Add cache for Account Visibility values.

Test: cts
Bug: 36485175
Change-Id: I075b81f8b287199d51ea09dc1108748310ab846c
parent 36a44f07
Loading
Loading
Loading
Loading
+78 −45
Original line number Diff line number Diff line
@@ -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,6 +753,25 @@ public class AccountManagerService
                packagesToVisibility = new HashMap<>();
            }

            if (!updateAccountVisibilityLocked(account, packageName, newVisibility, accounts)) {
                return false;
            }

            if (notify) {
                for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
                    if (packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE) {
                        notifyPackage(packageToVisibility.getKey(), accounts);
                    }
                }
                sendAccountsChangedBroadcast(accounts.userId);
            }
            return true;
        }
    }

    // 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;
@@ -765,18 +786,11 @@ public class AccountManagerService
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }

            if (notify) {
                for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
                    if (packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE) {
                        notifyPackage(packageToVisibility.getKey(), accounts);
                    }
                }
                sendAccountsChangedBroadcast(accounts.userId);
            }
        Map<String, Integer> accountVisibility =
            getPackagesAndVisibilityForAccountLocked(account, accounts);
        accountVisibility.put(packageName, newVisibility);
        return true;
    }
    }

    @Override
    public void registerAccountListener(String[] accountTypes, String opPackageName) {
@@ -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,18 +1199,30 @@ public class AccountManagerService
    }

    private void removeVisibilityValuesForPackage(String packageName) {
        if (isSpecialPackageKey(packageName)) {
            return;
        }
        synchronized (mUsers) {
          for (int i = 0; i < mUsers.size(); i++) {
            int numberOfUsers = mUsers.size();
            for (int i = 0; i < numberOfUsers; i++) {
                UserAccounts accounts = mUsers.valueAt(i);
                try {
                  int uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
                    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);
@@ -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);
    }

    /**
+37 −2
Original line number Diff line number Diff line
@@ -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 + "=? ",
+1 −1
Original line number Diff line number Diff line
@@ -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>
+36 −0
Original line number Diff line number Diff line
@@ -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;