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

Commit 795a8057 authored by Dmitry Dementyev's avatar Dmitry Dementyev Committed by Android (Google) Code Review
Browse files

Merge "Calculate how much memory is used per account." into tm-dev

parents b911560c eb0340b0
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
@@ -185,6 +185,8 @@ public class AccountManagerService
    final MessageHandler mHandler;

    private static final int TIMEOUT_DELAY_MS = 1000 * 60 * 15;
    private static final int MAXIMUM_PASSWORD_LENGTH = 1000 * 1000;
    private static final int STORAGE_LIMIT_PER_USER = 30 * 1000 * 1000;
    // Messages that can be sent on mHandler
    private static final int MESSAGE_TIMED_OUT = 3;
    private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
@@ -224,6 +226,8 @@ public class AccountManagerService
        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 #cacheLock} */
        private final Map<Account, Integer> mCacheSizeForAccount = new HashMap<>();

        /** protected by the {@link #mReceiversForType},
         *  type -> (packageName -> number of active receivers)
@@ -1108,6 +1112,65 @@ public class AccountManagerService
        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
    }

    private int computeEntrySize(@Nullable String key, @Nullable String value) {
        int keySize = key != null ? key.length() : 1;
        int valueSize = value != null ? value.length() : 1;
        return keySize + valueSize + 20;
    }

    /**
     * Restricts write operation if account uses too much storage.
     * Protected by the {@code cacheLock}
     */
    private boolean shouldBlockDatabaseWrite(UserAccounts accounts, Account account,
            @Nullable String key, @Nullable String value) {
        int usedStorage = accounts.mCacheSizeForAccount.getOrDefault(account, 0);
        // Estimation is not precise for updates to existing values.
        usedStorage = usedStorage + computeEntrySize(key, value);
        accounts.mCacheSizeForAccount.put(account, usedStorage);
        if (usedStorage < STORAGE_LIMIT_PER_USER / 100) {
            return false; // 100 is the upper bound for total number of accounts.
        }
        long numberOfAccounts = 0;
        for (Account[] accountsPerType : accounts.accountCache.values()) {
            if (accountsPerType != null) {
                numberOfAccounts = numberOfAccounts + accountsPerType.length;
            }
        }
        numberOfAccounts = numberOfAccounts != 0 ? numberOfAccounts : 1; // avoid division by zero.
        if (usedStorage < STORAGE_LIMIT_PER_USER / numberOfAccounts) {
            return false;
        }
        // Get more precise estimation of the  used storage before blocking operation.
        recomputeCacheSizeForAccountLocked(accounts, account);
        usedStorage = accounts.mCacheSizeForAccount.getOrDefault(account, 0);
        usedStorage = usedStorage + computeEntrySize(key, value);
        accounts.mCacheSizeForAccount.put(account, usedStorage);
        if (usedStorage < STORAGE_LIMIT_PER_USER / numberOfAccounts) {
            return false;
        }
        Log.w(TAG, "Account of type=" + account.type + " uses too much storage: " + usedStorage);
        return true;
    }

    /** protected by the {@code cacheLock} */
    private void recomputeCacheSizeForAccountLocked(UserAccounts accounts, Account account) {
        Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
        Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
        int usedStorage = 0;
        if (userDataForAccount != null) {
            for (Map.Entry<String, String> entry : userDataForAccount.entrySet()) {
                usedStorage = usedStorage + computeEntrySize(entry.getKey(), entry.getValue());
            }
        }
        if (authTokensForAccount != null) {
            for (Map.Entry<String, String> entry : authTokensForAccount.entrySet()) {
                usedStorage = usedStorage + computeEntrySize(entry.getKey(), entry.getValue());
            }
        }
        accounts.mCacheSizeForAccount.put(account, usedStorage);
    }

    /**
     * Validate internal set of accounts against installed authenticators for
     * given user. Clear cached authenticators before validating when requested.
@@ -1225,6 +1288,7 @@ public class AccountManagerService
                            accounts.authTokenCache.remove(account);
                            accounts.accountTokenCaches.remove(account);
                            accounts.visibilityCache.remove(account);
                            accounts.mCacheSizeForAccount.remove(account);

                            for (Entry<String, Integer> packageToVisibility :
                                    packagesToVisibility.entrySet()) {
@@ -1834,6 +1898,10 @@ public class AccountManagerService
            Log.w(TAG, "Account cannot be added - Name longer than 200 chars");
            return false;
        }
        if (password != null && password.length() > MAXIMUM_PASSWORD_LENGTH) {
            Log.w(TAG, "Account cannot be added - password is too long");
            return false;
        }
        if (!isLocalUnlockedUser(accounts.userId)) {
            Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
                    + accounts.userId + " is locked. callingUid=" + callingUid);
@@ -2187,6 +2255,7 @@ public class AccountManagerService
                        renamedAccount,
                        new AtomicReference<>(accountToRename.name));
                resultAccount = renamedAccount;
                recomputeCacheSizeForAccountLocked(accounts, renamedAccount);

                int parentUserId = accounts.userId;
                if (canHaveProfile(parentUserId)) {
@@ -2569,6 +2638,10 @@ public class AccountManagerService
        cancelNotification(getSigninRequiredNotificationId(accounts, account),
                UserHandle.of(accounts.userId));
        synchronized (accounts.dbLock) {
            boolean shouldBlockWrite = false;
            synchronized (accounts.cacheLock) {
                shouldBlockWrite = shouldBlockDatabaseWrite(accounts, account, type, authToken);
            }
            accounts.accountsDb.beginTransaction();
            boolean updateCache = false;
            try {
@@ -2577,6 +2650,11 @@ public class AccountManagerService
                    return false;
                }
                accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
                if (authToken != null && shouldBlockWrite) {
                    Log.w(TAG, "Too much storage is used - block token update for accountType="
                            + account.type);
                    return false; // fail silently.
                }
                if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
                    accounts.accountsDb.setTransactionSuccessful();
                    updateCache = true;
@@ -2686,6 +2764,10 @@ public class AccountManagerService
        if (account == null) {
            return;
        }
        if (password != null && password.length() > MAXIMUM_PASSWORD_LENGTH) {
            Log.w(TAG, "New password is too long for accountType=" + account.type);
            return;
        }
        boolean isChanged = false;
        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
@@ -2794,6 +2876,14 @@ public class AccountManagerService
    private void setUserdataInternal(UserAccounts accounts, Account account, String key,
            String value) {
        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
                if (value != null && shouldBlockDatabaseWrite(accounts, account, key, value)) {
                    Log.w(TAG, "Too much storage is used - block user data update for accountType="
                            + account.type);
                    return; // fail silently.
                }
            }

            accounts.accountsDb.beginTransaction();
            try {
                long accountId = accounts.accountsDb.findDeAccountId(account);
@@ -6024,6 +6114,7 @@ public class AccountManagerService
        accounts.authTokenCache.remove(account);
        accounts.previousNameCache.remove(account);
        accounts.visibilityCache.remove(account);
        accounts.mCacheSizeForAccount.remove(account);

        AccountManager.invalidateLocalAccountsDataCaches();
    }
@@ -6201,15 +6292,20 @@ public class AccountManagerService
    protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
            Account account, String key, String value) {
        Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
        boolean updateCacheSize = false;
        if (userDataForAccount == null) {
            userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
            accounts.userDataCache.put(account, userDataForAccount);
            updateCacheSize = true;
        }
        if (value == null) {
            userDataForAccount.remove(key);
        } else {
            userDataForAccount.put(key, value);
        }
        if (updateCacheSize) {
            recomputeCacheSizeForAccountLocked(accounts, account);
        }
    }

    protected TokenCache.Value readCachedTokenInternal(
@@ -6228,15 +6324,20 @@ public class AccountManagerService
    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
            Account account, String key, String value) {
        Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
        boolean updateCacheSize = false;
        if (authTokensForAccount == null) {
            authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
            accounts.authTokenCache.put(account, authTokensForAccount);
            updateCacheSize = true;
        }
        if (value == null) {
            authTokensForAccount.remove(key);
        } else {
            authTokensForAccount.put(key, value);
        }
        if (updateCacheSize) {
            recomputeCacheSizeForAccountLocked(accounts, account);
        }
    }

    protected String readAuthTokenInternal(UserAccounts accounts, Account account,
@@ -6256,6 +6357,7 @@ public class AccountManagerService
                    // need to populate the cache for this account
                    authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
                    accounts.authTokenCache.put(account, authTokensForAccount);
                    recomputeCacheSizeForAccountLocked(accounts, account);
                }
                return authTokensForAccount.get(authTokenType);
            }
@@ -6277,6 +6379,7 @@ public class AccountManagerService
                        // need to populate the cache for this account
                        userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
                        accounts.userDataCache.put(account, userDataForAccount);
                        recomputeCacheSizeForAccountLocked(accounts, account);
                    }
                }
            }