1751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistpackage com.android.server.wifi.hotspot2; 2751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 3751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport android.util.Log; 4751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 5ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpandeimport com.android.server.wifi.Clock; 6751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport com.android.server.wifi.anqp.ANQPElement; 7751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport com.android.server.wifi.anqp.Constants; 8751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 99f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvistimport java.io.PrintWriter; 10751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.ArrayList; 11751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.HashMap; 12751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.List; 13751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.Map; 14751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 15c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvistpublic class AnqpCache { 16af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato private static final boolean DBG = false; 17af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato 18751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist private static final long CACHE_RECHECK = 60000L; 19a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static final boolean STANDARD_ESS = true; // Regular AP keying; see CacheKey below. 20c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist private long mLastSweep; 21ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande private Clock mClock; 22751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 23ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private final HashMap<CacheKey, ANQPData> mANQPCache; 24751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 25ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande public AnqpCache(Clock clock) { 26ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande mClock = clock; 27751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist mANQPCache = new HashMap<>(); 28ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande mLastSweep = mClock.currentTimeMillis(); 29751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 30751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 31ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private static class CacheKey { 32ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private final String mSSID; 33ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private final long mBSSID; 34ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private final long mHESSID; 35ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 36ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private CacheKey(String ssid, long bssid, long hessid) { 37ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist mSSID = ssid; 38ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist mBSSID = bssid; 39ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist mHESSID = hessid; 40ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 41ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 42ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist /** 43ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * Build an ANQP cache key suitable for the granularity of the key space as follows: 44ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * 45ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * HESSID domainID standardESS Key content Rationale 46ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * -------- ----------- --------------- ----------- -------------------- 47ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * n/a zero n/a SSID/BSSID Domain ID indicates unique AP info 48ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * not set set false SSID/BSSID Strict per AP keying override 49ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * not set set true SSID Standard definition of an ESS 50ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * set set n/a HESSID The ESS is defined by the HESSID 51ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * 52ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * @param network The network to build the key for. 53ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used 54ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * for the cache, i.e. all APs with identical SSID is considered an ESS, 55ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * otherwise caching is performed per AP. 56ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist * @return A CacheKey. 57ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist */ 58ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist private static CacheKey buildKey(NetworkDetail network, boolean standardESS) { 59ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist String ssid; 60ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist long bssid; 61ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist long hessid; 62ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) { 63ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist ssid = network.getSSID(); 64ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist bssid = network.getBSSID(); 65ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist hessid = 0L; 66ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 67ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) { 68ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist ssid = null; 69ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist bssid = 0L; 70ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist hessid = network.getHESSID(); 71ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 72ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else { 73ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist ssid = network.getSSID(); 74ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist bssid = 0L; 75ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist hessid = 0L; 76ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 77ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 78ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return new CacheKey(ssid, bssid, hessid); 79ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 80ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 81ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist @Override 82ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist public int hashCode() { 83ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist if (mHESSID != 0) { 84ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return (int)((mHESSID >>> 32) * 31 + mHESSID); 85ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 86ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else if (mBSSID != 0) { 87ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID); 88ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 89ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else { 90ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return mSSID.hashCode(); 91ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 92ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 93ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 94ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist @Override 95ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist public boolean equals(Object thatObject) { 96ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist if (thatObject == this) { 97ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return true; 98ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 99ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else if (thatObject == null || thatObject.getClass() != CacheKey.class) { 100ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return false; 101ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 102ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist CacheKey that = (CacheKey) thatObject; 103ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return Utils.compare(that.mSSID, mSSID) == 0 && 104ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist that.mBSSID == mBSSID && 105ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist that.mHESSID == mHESSID; 106ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 107ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 108ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist @Override 109ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist public String toString() { 110ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist if (mHESSID != 0L) { 111ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return "HESSID:" + NetworkDetail.toMACString(mHESSID); 112ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 113ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else if (mBSSID != 0L) { 114ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return NetworkDetail.toMACString(mBSSID) + 115ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist ":<" + Utils.toUnicodeEscapedString(mSSID) + ">"; 116ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 117ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist else { 118ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist return '<' + Utils.toUnicodeEscapedString(mSSID) + '>'; 119ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 120ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 121ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist } 122ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 1231d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist public List<Constants.ANQPElementType> initiate(NetworkDetail network, 1241d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist List<Constants.ANQPElementType> querySet) { 125ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 126ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist 127751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist synchronized (mANQPCache) { 128ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist ANQPData data = mANQPCache.get(key); 1295bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist if (data == null || data.expired()) { 130ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande mANQPCache.put(key, new ANQPData(mClock, network, data)); 1311d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist return querySet; 132751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 133751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist else { 1341d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist List<Constants.ANQPElementType> newList = data.disjoint(querySet); 1355bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist Log.d(Utils.hs2LogTag(getClass()), 1361d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist String.format("New ANQP elements for BSSID %012x: %s", 1371d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist network.getBSSID(), newList)); 1381d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist return newList; 139751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 140751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 141751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 142751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 143751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist public void update(NetworkDetail network, 144751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 145751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 146ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 147751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 148751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just 149751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist // long enough to prevent excessive re-querying. 150751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist synchronized (mANQPCache) { 1511d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist ANQPData data = mANQPCache.get(key); 1521d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist if (data != null && data.hasData()) { 1531d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist data.merge(anqpElements); 1541d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist } 1551d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist else { 156ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande data = new ANQPData(mClock, network, anqpElements); 1571d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist mANQPCache.put(key, data); 1581d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist } 159751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 160751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 161751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 162751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist public ANQPData getEntry(NetworkDetail network) { 163751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist ANQPData data; 164751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 165ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 166751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist synchronized (mANQPCache) { 167ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist data = mANQPCache.get(key); 168751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 169751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 1705bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist return data != null && data.isValid(network) ? data : null; 171751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 172751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist 173c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist public void clear(boolean all, boolean debug) { 174af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato if (DBG) Log.d(Utils.hs2LogTag(getClass()), "Clearing ANQP cache: all: " + all); 175ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande long now = mClock.currentTimeMillis(); 176751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist synchronized (mANQPCache) { 177c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist if (all) { 178c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist mANQPCache.clear(); 179c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist mLastSweep = now; 180751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 181c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist else if (now > mLastSweep + CACHE_RECHECK) { 182c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist List<CacheKey> retirees = new ArrayList<>(); 183c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) { 184c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist if (entry.getValue().expired(now)) { 185c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist retirees.add(entry.getKey()); 186c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist } 187c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist } 188c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist for (CacheKey key : retirees) { 189c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist mANQPCache.remove(key); 190c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist if (debug) { 191c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist Log.d(Utils.hs2LogTag(getClass()), "Retired " + key); 192c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist } 193c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist } 194c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist mLastSweep = now; 195751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 196751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 197751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist } 1989f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist 1999f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist public void dump(PrintWriter out) { 200ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande out.println("Last sweep " + Utils.toHMS(mClock.currentTimeMillis() - mLastSweep) + " ago."); 2019f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist for (ANQPData anqpData : mANQPCache.values()) { 2029f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist out.println(anqpData.toString(false)); 2039f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist } 2049f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist } 205751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist} 206