191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia/* 291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Copyright (C) 2015 The Android Open Source Project 391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * 491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Licensed under the Apache License, Version 2.0 (the "License"); 591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * you may not use this file except in compliance with the License. 691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * You may obtain a copy of the License at 791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * 891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * http://www.apache.org/licenses/LICENSE-2.0 991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * 1091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Unless required by applicable law or agreed to in writing, software 1191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * distributed under the License is distributed on an "AS IS" BASIS, 1291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * See the License for the specific language governing permissions and 1491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * limitations under the License. 1591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia */ 1691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 1791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviapackage com.android.server.accounts; 1891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 19c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdiviaimport android.accounts.Account; 20c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdiviaimport android.util.LruCache; 21c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdiviaimport android.util.Pair; 22c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 23c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdiviaimport com.android.internal.util.Preconditions; 24c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 2591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviaimport java.util.ArrayList; 2691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviaimport java.util.Arrays; 2791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviaimport java.util.HashMap; 2891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviaimport java.util.List; 2991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdiviaimport java.util.Objects; 3091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 3191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia/** 32c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * TokenCaches manage time limited authentication tokens in memory. 3391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia */ 3491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia/* default */ class TokenCache { 3591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 36c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private static final int MAX_CACHE_CHARS = 64000; 37c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 3891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia private static class Value { 3991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public final String token; 4091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public final long expiryEpochMillis; 4191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 4291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public Value(String token, long expiryEpochMillis) { 4391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia this.token = token; 4491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia this.expiryEpochMillis = expiryEpochMillis; 4591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 4691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 4791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 4891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia private static class Key { 49c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public final Account account; 5091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public final String packageName; 5191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public final String tokenType; 5291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public final byte[] sigDigest; 5391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 54c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public Key(Account account, String tokenType, String packageName, byte[] sigDigest) { 55c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia this.account = account; 5691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia this.tokenType = tokenType; 5791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia this.packageName = packageName; 5891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia this.sigDigest = sigDigest; 5991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 6091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 6191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia @Override 6291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public boolean equals(Object o) { 6391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia if (o != null && o instanceof Key) { 6491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia Key cacheKey = (Key) o; 65c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia return Objects.equals(account, cacheKey.account) 66c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia && Objects.equals(packageName, cacheKey.packageName) 6791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia && Objects.equals(tokenType, cacheKey.tokenType) 6891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia && Arrays.equals(sigDigest, cacheKey.sigDigest); 6991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } else { 7091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia return false; 7191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 7291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 7391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 7491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia @Override 7591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public int hashCode() { 76c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia return account.hashCode() 77c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia ^ packageName.hashCode() 78c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia ^ tokenType.hashCode() 79c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia ^ Arrays.hashCode(sigDigest); 8091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 8191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 8291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 83c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private static class TokenLruCache extends LruCache<Key, Value> { 8491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 85c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private class Evictor { 86c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private final List<Key> mKeys; 87c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 88c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public Evictor() { 89c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mKeys = new ArrayList<>(); 90c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 91c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 92c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void add(Key k) { 93c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mKeys.add(k); 94c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 95c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 96c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void evict() { 97c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia for (Key k : mKeys) { 98c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia TokenLruCache.this.remove(k); 99c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 100c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 101c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 102c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 103c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia /** 104c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * Map associated tokens with an Evictor that will manage evicting the token from the 105c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * cache. This reverse lookup is needed because very little information is given at token 106c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * invalidation time. 107c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia */ 108c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private HashMap<Pair<String, String>, Evictor> mTokenEvictors = new HashMap<>(); 109c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private HashMap<Account, Evictor> mAccountEvictors = new HashMap<>(); 110c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 111c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public TokenLruCache() { 112c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia super(MAX_CACHE_CHARS); 113c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 114c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 115c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia @Override 116c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia protected int sizeOf(Key k, Value v) { 117c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia return v.token.length(); 118c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 119c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 120c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia @Override 121c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia protected void entryRemoved(boolean evicted, Key k, Value oldVal, Value newVal) { 122c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia // When a token has been removed, clean up the associated Evictor. 123c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (oldVal != null && newVal == null) { 124c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia /* 125c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * This is recursive, but it won't spiral out of control because LruCache is 126c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * thread safe and the Evictor can only be removed once. 127c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia */ 128c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Evictor evictor = mTokenEvictors.remove(oldVal.token); 129c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (evictor != null) { 130c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia evictor.evict(); 131c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 132c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 133c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 13491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 135c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void putToken(Key k, Value v) { 136c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia // Prepare for removal by token string. 137c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Evictor tokenEvictor = mTokenEvictors.get(v.token); 138c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (tokenEvictor == null) { 139c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia tokenEvictor = new Evictor(); 140c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 141c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia tokenEvictor.add(k); 142c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mTokenEvictors.put(new Pair<>(k.account.type, v.token), tokenEvictor); 14391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 144c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia // Prepare for removal by associated account. 145c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Evictor accountEvictor = mAccountEvictors.get(k.account); 146c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (accountEvictor == null) { 147c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia accountEvictor = new Evictor(); 148c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 149c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia accountEvictor.add(k); 150c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mAccountEvictors.put(k.account, tokenEvictor); 151c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 152c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia // Only cache the token once we can remove it directly or by account. 153c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia put(k, v); 15491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 15591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 156c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void evict(String accountType, String token) { 157c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Evictor evictor = mTokenEvictors.get(new Pair<>(accountType, token)); 158c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (evictor != null) { 159c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia evictor.evict(); 160c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 161c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 16291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 16391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 164c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void evict(Account account) { 165c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Evictor evictor = mAccountEvictors.get(account); 166c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia if (evictor != null) { 167c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia evictor.evict(); 16891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 16991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 17091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 17191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 17291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia /** 173c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * Map associating basic token lookup information with with actual tokens (and optionally their 174c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia * expiration times). 175c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia */ 176c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia private TokenLruCache mCachedTokens = new TokenLruCache(); 177c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 178c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia /** 17991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Caches the specified token until the specified expiryMillis. The token will be associated 18091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * with the given token type, package name, and digest of signatures. 18191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * 18291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * @param token 18391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * @param tokenType 18491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * @param packageName 18591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * @param sigDigest 18691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * @param expiryMillis 18791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia */ 18891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia public void put( 189c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Account account, 19091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia String token, 19191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia String tokenType, 19291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia String packageName, 19391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia byte[] sigDigest, 19491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia long expiryMillis) { 195c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Preconditions.checkNotNull(account); 19691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia if (token == null || System.currentTimeMillis() > expiryMillis) { 19791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia return; 19891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 199c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Key k = new Key(account, tokenType, packageName, sigDigest); 20091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia Value v = new Value(token, expiryMillis); 201c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mCachedTokens.putToken(k, v); 20291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 20391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 20491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia /** 20591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Evicts the specified token from the cache. This should be called as part of a token 20691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * invalidation workflow. 20791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia */ 208c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void remove(String accountType, String token) { 209c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mCachedTokens.evict(accountType, token); 210c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia } 211c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia 212c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public void remove(Account account) { 213c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia mCachedTokens.evict(account); 21491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 21591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia 21691979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia /** 21791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia * Gets a token from the cache if possible. 21891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia */ 219c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia public String get(Account account, String tokenType, String packageName, byte[] sigDigest) { 220c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia Key k = new Key(account, tokenType, packageName, sigDigest); 22191979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia Value v = mCachedTokens.get(k); 22291979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia long currentTime = System.currentTimeMillis(); 22391979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia if (v != null && currentTime < v.expiryEpochMillis) { 22491979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia return v.token; 22591979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } else if (v != null) { 226c37ee22714ddec1104ba3a2189cf77924ac27812Carlos Valdivia remove(account.type, v.token); 22791979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 22891979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia return null; 22991979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia } 23091979be8804232a04da2bf36cdd857ee7da04479Carlos Valdivia} 231