Loading core/java/android/accounts/AccountManager.java +4 −2 Original line number Diff line number Diff line Loading @@ -489,7 +489,8 @@ public class AccountManager { * <p>It is safe to call this method from the main thread. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#GET_ACCOUNTS}. * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the * authenticator that owns the account type. * * @param type The type of accounts to return, null to retrieve all accounts * @return An array of {@link Account}, one per matching account. Empty Loading Loading @@ -615,7 +616,8 @@ public class AccountManager { * {@link AccountManagerFuture} must not be used on the main thread. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#GET_ACCOUNTS}. * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the * authenticator that owns the account type. * * @param type The type of accounts to return, must not be null * @param features An array of the account features to require, Loading services/core/java/com/android/server/accounts/AccountManagerService.java +158 −121 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/accounts/TokenCache.java +115 −48 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package com.android.server.accounts; import android.accounts.Account; import android.util.LruCache; import android.util.Pair; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; Loading @@ -23,10 +29,12 @@ import java.util.List; import java.util.Objects; /** * TokenCaches manage tokens associated with an account in memory. * TokenCaches manage time limited authentication tokens in memory. */ /* default */ class TokenCache { private static final int MAX_CACHE_CHARS = 64000; private static class Value { public final String token; public final long expiryEpochMillis; Loading @@ -38,11 +46,13 @@ import java.util.Objects; } private static class Key { public final Account account; public final String packageName; public final String tokenType; public final byte[] sigDigest; public Key(String tokenType, String packageName, byte[] sigDigest) { public Key(Account account, String tokenType, String packageName, byte[] sigDigest) { this.account = account; this.tokenType = tokenType; this.packageName = packageName; this.sigDigest = sigDigest; Loading @@ -52,7 +62,8 @@ import java.util.Objects; public boolean equals(Object o) { if (o != null && o instanceof Key) { Key cacheKey = (Key) o; return Objects.equals(packageName, cacheKey.packageName) return Objects.equals(account, cacheKey.account) && Objects.equals(packageName, cacheKey.packageName) && Objects.equals(tokenType, cacheKey.tokenType) && Arrays.equals(sigDigest, cacheKey.sigDigest); } else { Loading @@ -62,30 +73,20 @@ import java.util.Objects; @Override public int hashCode() { return packageName.hashCode() ^ tokenType.hashCode() ^ Arrays.hashCode(sigDigest); return account.hashCode() ^ packageName.hashCode() ^ tokenType.hashCode() ^ Arrays.hashCode(sigDigest); } } /** * Map associating basic token lookup information with with actual tokens (and optionally their * expiration times). */ private HashMap<Key, Value> mCachedTokens = new HashMap<>(); /** * Map associated tokens with an Evictor that will manage evicting the token from the cache. * This reverse lookup is needed because very little information is given at token invalidation * time. */ private HashMap<String, Evictor> mTokenEvictors = new HashMap<>(); private static class TokenLruCache extends LruCache<Key, Value> { private class Evictor { private final String mToken; private final List<Key> mKeys; public Evictor(String token) { public Evictor() { mKeys = new ArrayList<>(); mToken = token; } public void add(Key k) { Loading @@ -94,13 +95,86 @@ import java.util.Objects; public void evict() { for (Key k : mKeys) { mCachedTokens.remove(k); TokenLruCache.this.remove(k); } // Clear out the evictor reference. mTokenEvictors.remove(mToken); } } /** * Map associated tokens with an Evictor that will manage evicting the token from the * cache. This reverse lookup is needed because very little information is given at token * invalidation time. */ private HashMap<Pair<String, String>, Evictor> mTokenEvictors = new HashMap<>(); private HashMap<Account, Evictor> mAccountEvictors = new HashMap<>(); public TokenLruCache() { super(MAX_CACHE_CHARS); } @Override protected int sizeOf(Key k, Value v) { return v.token.length(); } @Override protected void entryRemoved(boolean evicted, Key k, Value oldVal, Value newVal) { // When a token has been removed, clean up the associated Evictor. if (oldVal != null && newVal == null) { /* * This is recursive, but it won't spiral out of control because LruCache is * thread safe and the Evictor can only be removed once. */ Evictor evictor = mTokenEvictors.remove(oldVal.token); if (evictor != null) { evictor.evict(); } } } public void putToken(Key k, Value v) { // Prepare for removal by token string. Evictor tokenEvictor = mTokenEvictors.get(v.token); if (tokenEvictor == null) { tokenEvictor = new Evictor(); } tokenEvictor.add(k); mTokenEvictors.put(new Pair<>(k.account.type, v.token), tokenEvictor); // Prepare for removal by associated account. Evictor accountEvictor = mAccountEvictors.get(k.account); if (accountEvictor == null) { accountEvictor = new Evictor(); } accountEvictor.add(k); mAccountEvictors.put(k.account, tokenEvictor); // Only cache the token once we can remove it directly or by account. put(k, v); } public void evict(String accountType, String token) { Evictor evictor = mTokenEvictors.get(new Pair<>(accountType, token)); if (evictor != null) { evictor.evict(); } } public void evict(Account account) { Evictor evictor = mAccountEvictors.get(account); if (evictor != null) { evictor.evict(); } } } /** * Map associating basic token lookup information with with actual tokens (and optionally their * expiration times). */ private TokenLruCache mCachedTokens = new TokenLruCache(); /** * Caches the specified token until the specified expiryMillis. The token will be associated * with the given token type, package name, and digest of signatures. Loading @@ -112,51 +186,44 @@ import java.util.Objects; * @param expiryMillis */ public void put( Account account, String token, String tokenType, String packageName, byte[] sigDigest, long expiryMillis) { Preconditions.checkNotNull(account); if (token == null || System.currentTimeMillis() > expiryMillis) { return; } Key k = new Key(tokenType, packageName, sigDigest); // Prep evictor. No token should be cached without a corresponding evictor. Evictor evictor = mTokenEvictors.get(token); if (evictor == null) { evictor = new Evictor(token); } evictor.add(k); mTokenEvictors.put(token, evictor); // Then cache values. Key k = new Key(account, tokenType, packageName, sigDigest); Value v = new Value(token, expiryMillis); mCachedTokens.put(k, v); mCachedTokens.putToken(k, v); } /** * Evicts the specified token from the cache. This should be called as part of a token * invalidation workflow. */ public void remove(String token) { Evictor evictor = mTokenEvictors.get(token); if (evictor == null) { // This condition is expected if the token isn't cached. return; public void remove(String accountType, String token) { mCachedTokens.evict(accountType, token); } evictor.evict(); public void remove(Account account) { mCachedTokens.evict(account); } /** * Gets a token from the cache if possible. */ public String get(String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(tokenType, packageName, sigDigest); public String get(Account account, String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(account, tokenType, packageName, sigDigest); Value v = mCachedTokens.get(k); long currentTime = System.currentTimeMillis(); if (v != null && currentTime < v.expiryEpochMillis) { return v.token; } else if (v != null) { remove(v.token); remove(account.type, v.token); } return null; } Loading Loading
core/java/android/accounts/AccountManager.java +4 −2 Original line number Diff line number Diff line Loading @@ -489,7 +489,8 @@ public class AccountManager { * <p>It is safe to call this method from the main thread. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#GET_ACCOUNTS}. * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the * authenticator that owns the account type. * * @param type The type of accounts to return, null to retrieve all accounts * @return An array of {@link Account}, one per matching account. Empty Loading Loading @@ -615,7 +616,8 @@ public class AccountManager { * {@link AccountManagerFuture} must not be used on the main thread. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#GET_ACCOUNTS}. * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the * authenticator that owns the account type. * * @param type The type of accounts to return, must not be null * @param features An array of the account features to require, Loading
services/core/java/com/android/server/accounts/AccountManagerService.java +158 −121 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/accounts/TokenCache.java +115 −48 Original line number Diff line number Diff line Loading @@ -16,6 +16,12 @@ package com.android.server.accounts; import android.accounts.Account; import android.util.LruCache; import android.util.Pair; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; Loading @@ -23,10 +29,12 @@ import java.util.List; import java.util.Objects; /** * TokenCaches manage tokens associated with an account in memory. * TokenCaches manage time limited authentication tokens in memory. */ /* default */ class TokenCache { private static final int MAX_CACHE_CHARS = 64000; private static class Value { public final String token; public final long expiryEpochMillis; Loading @@ -38,11 +46,13 @@ import java.util.Objects; } private static class Key { public final Account account; public final String packageName; public final String tokenType; public final byte[] sigDigest; public Key(String tokenType, String packageName, byte[] sigDigest) { public Key(Account account, String tokenType, String packageName, byte[] sigDigest) { this.account = account; this.tokenType = tokenType; this.packageName = packageName; this.sigDigest = sigDigest; Loading @@ -52,7 +62,8 @@ import java.util.Objects; public boolean equals(Object o) { if (o != null && o instanceof Key) { Key cacheKey = (Key) o; return Objects.equals(packageName, cacheKey.packageName) return Objects.equals(account, cacheKey.account) && Objects.equals(packageName, cacheKey.packageName) && Objects.equals(tokenType, cacheKey.tokenType) && Arrays.equals(sigDigest, cacheKey.sigDigest); } else { Loading @@ -62,30 +73,20 @@ import java.util.Objects; @Override public int hashCode() { return packageName.hashCode() ^ tokenType.hashCode() ^ Arrays.hashCode(sigDigest); return account.hashCode() ^ packageName.hashCode() ^ tokenType.hashCode() ^ Arrays.hashCode(sigDigest); } } /** * Map associating basic token lookup information with with actual tokens (and optionally their * expiration times). */ private HashMap<Key, Value> mCachedTokens = new HashMap<>(); /** * Map associated tokens with an Evictor that will manage evicting the token from the cache. * This reverse lookup is needed because very little information is given at token invalidation * time. */ private HashMap<String, Evictor> mTokenEvictors = new HashMap<>(); private static class TokenLruCache extends LruCache<Key, Value> { private class Evictor { private final String mToken; private final List<Key> mKeys; public Evictor(String token) { public Evictor() { mKeys = new ArrayList<>(); mToken = token; } public void add(Key k) { Loading @@ -94,13 +95,86 @@ import java.util.Objects; public void evict() { for (Key k : mKeys) { mCachedTokens.remove(k); TokenLruCache.this.remove(k); } // Clear out the evictor reference. mTokenEvictors.remove(mToken); } } /** * Map associated tokens with an Evictor that will manage evicting the token from the * cache. This reverse lookup is needed because very little information is given at token * invalidation time. */ private HashMap<Pair<String, String>, Evictor> mTokenEvictors = new HashMap<>(); private HashMap<Account, Evictor> mAccountEvictors = new HashMap<>(); public TokenLruCache() { super(MAX_CACHE_CHARS); } @Override protected int sizeOf(Key k, Value v) { return v.token.length(); } @Override protected void entryRemoved(boolean evicted, Key k, Value oldVal, Value newVal) { // When a token has been removed, clean up the associated Evictor. if (oldVal != null && newVal == null) { /* * This is recursive, but it won't spiral out of control because LruCache is * thread safe and the Evictor can only be removed once. */ Evictor evictor = mTokenEvictors.remove(oldVal.token); if (evictor != null) { evictor.evict(); } } } public void putToken(Key k, Value v) { // Prepare for removal by token string. Evictor tokenEvictor = mTokenEvictors.get(v.token); if (tokenEvictor == null) { tokenEvictor = new Evictor(); } tokenEvictor.add(k); mTokenEvictors.put(new Pair<>(k.account.type, v.token), tokenEvictor); // Prepare for removal by associated account. Evictor accountEvictor = mAccountEvictors.get(k.account); if (accountEvictor == null) { accountEvictor = new Evictor(); } accountEvictor.add(k); mAccountEvictors.put(k.account, tokenEvictor); // Only cache the token once we can remove it directly or by account. put(k, v); } public void evict(String accountType, String token) { Evictor evictor = mTokenEvictors.get(new Pair<>(accountType, token)); if (evictor != null) { evictor.evict(); } } public void evict(Account account) { Evictor evictor = mAccountEvictors.get(account); if (evictor != null) { evictor.evict(); } } } /** * Map associating basic token lookup information with with actual tokens (and optionally their * expiration times). */ private TokenLruCache mCachedTokens = new TokenLruCache(); /** * Caches the specified token until the specified expiryMillis. The token will be associated * with the given token type, package name, and digest of signatures. Loading @@ -112,51 +186,44 @@ import java.util.Objects; * @param expiryMillis */ public void put( Account account, String token, String tokenType, String packageName, byte[] sigDigest, long expiryMillis) { Preconditions.checkNotNull(account); if (token == null || System.currentTimeMillis() > expiryMillis) { return; } Key k = new Key(tokenType, packageName, sigDigest); // Prep evictor. No token should be cached without a corresponding evictor. Evictor evictor = mTokenEvictors.get(token); if (evictor == null) { evictor = new Evictor(token); } evictor.add(k); mTokenEvictors.put(token, evictor); // Then cache values. Key k = new Key(account, tokenType, packageName, sigDigest); Value v = new Value(token, expiryMillis); mCachedTokens.put(k, v); mCachedTokens.putToken(k, v); } /** * Evicts the specified token from the cache. This should be called as part of a token * invalidation workflow. */ public void remove(String token) { Evictor evictor = mTokenEvictors.get(token); if (evictor == null) { // This condition is expected if the token isn't cached. return; public void remove(String accountType, String token) { mCachedTokens.evict(accountType, token); } evictor.evict(); public void remove(Account account) { mCachedTokens.evict(account); } /** * Gets a token from the cache if possible. */ public String get(String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(tokenType, packageName, sigDigest); public String get(Account account, String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(account, tokenType, packageName, sigDigest); Value v = mCachedTokens.get(k); long currentTime = System.currentTimeMillis(); if (v != null && currentTime < v.expiryEpochMillis) { return v.token; } else if (v != null) { remove(v.token); remove(account.type, v.token); } return null; } Loading