Loading services/core/java/com/android/server/accounts/AccountManagerService.java +103 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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. Loading Loading @@ -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()) { Loading Loading @@ -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); Loading Loading @@ -2187,6 +2255,7 @@ public class AccountManagerService renamedAccount, new AtomicReference<>(accountToRename.name)); resultAccount = renamedAccount; recomputeCacheSizeForAccountLocked(accounts, renamedAccount); int parentUserId = accounts.userId; if (canHaveProfile(parentUserId)) { Loading Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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(); } Loading Loading @@ -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( Loading @@ -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, Loading @@ -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); } Loading @@ -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); } } } Loading Loading
services/core/java/com/android/server/accounts/AccountManagerService.java +103 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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. Loading Loading @@ -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()) { Loading Loading @@ -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); Loading Loading @@ -2187,6 +2255,7 @@ public class AccountManagerService renamedAccount, new AtomicReference<>(accountToRename.name)); resultAccount = renamedAccount; recomputeCacheSizeForAccountLocked(accounts, renamedAccount); int parentUserId = accounts.userId; if (canHaveProfile(parentUserId)) { Loading Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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(); } Loading Loading @@ -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( Loading @@ -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, Loading @@ -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); } Loading @@ -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); } } } Loading