AnqpCache.java revision c465290772b735d97cba451c9bc5b6cc76db9147
1package com.android.server.wifi.hotspot2; 2 3import android.util.Log; 4 5import com.android.server.wifi.anqp.ANQPElement; 6import com.android.server.wifi.anqp.Constants; 7 8import java.util.ArrayList; 9import java.util.HashMap; 10import java.util.List; 11import java.util.Map; 12 13public class AnqpCache { 14 private static final long CACHE_RECHECK = 60000L; 15 private static final boolean STANDARD_ESS = true; // Regular AP keying; see CacheKey below. 16 private long mLastSweep; 17 18 private final HashMap<CacheKey, ANQPData> mANQPCache; 19 20 public AnqpCache() { 21 mANQPCache = new HashMap<>(); 22 mLastSweep = System.currentTimeMillis(); 23 } 24 25 private static class CacheKey { 26 private final String mSSID; 27 private final long mBSSID; 28 private final long mHESSID; 29 30 private CacheKey(String ssid, long bssid, long hessid) { 31 mSSID = ssid; 32 mBSSID = bssid; 33 mHESSID = hessid; 34 } 35 36 /** 37 * Build an ANQP cache key suitable for the granularity of the key space as follows: 38 * 39 * HESSID domainID standardESS Key content Rationale 40 * -------- ----------- --------------- ----------- -------------------- 41 * n/a zero n/a SSID/BSSID Domain ID indicates unique AP info 42 * not set set false SSID/BSSID Strict per AP keying override 43 * not set set true SSID Standard definition of an ESS 44 * set set n/a HESSID The ESS is defined by the HESSID 45 * 46 * @param network The network to build the key for. 47 * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used 48 * for the cache, i.e. all APs with identical SSID is considered an ESS, 49 * otherwise caching is performed per AP. 50 * @return A CacheKey. 51 */ 52 private static CacheKey buildKey(NetworkDetail network, boolean standardESS) { 53 String ssid; 54 long bssid; 55 long hessid; 56 if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) { 57 ssid = network.getSSID(); 58 bssid = network.getBSSID(); 59 hessid = 0L; 60 } 61 else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) { 62 ssid = null; 63 bssid = 0L; 64 hessid = network.getHESSID(); 65 } 66 else { 67 ssid = network.getSSID(); 68 bssid = 0L; 69 hessid = 0L; 70 } 71 72 return new CacheKey(ssid, bssid, hessid); 73 } 74 75 @Override 76 public int hashCode() { 77 if (mHESSID != 0) { 78 return (int)((mHESSID >>> 32) * 31 + mHESSID); 79 } 80 else if (mBSSID != 0) { 81 return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID); 82 } 83 else { 84 return mSSID.hashCode(); 85 } 86 } 87 88 @Override 89 public boolean equals(Object thatObject) { 90 if (thatObject == this) { 91 return true; 92 } 93 else if (thatObject == null || thatObject.getClass() != CacheKey.class) { 94 return false; 95 } 96 CacheKey that = (CacheKey) thatObject; 97 return Utils.compare(that.mSSID, mSSID) == 0 && 98 that.mBSSID == mBSSID && 99 that.mHESSID == mHESSID; 100 } 101 102 @Override 103 public String toString() { 104 if (mHESSID != 0L) { 105 return "HESSID:" + NetworkDetail.toMACString(mHESSID); 106 } 107 else if (mBSSID != 0L) { 108 return NetworkDetail.toMACString(mBSSID) + 109 ":<" + Utils.toUnicodeEscapedString(mSSID) + ">"; 110 } 111 else { 112 return '<' + Utils.toUnicodeEscapedString(mSSID) + '>'; 113 } 114 } 115 } 116 117 public boolean initiate(NetworkDetail network) { 118 CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 119 120 synchronized (mANQPCache) { 121 ANQPData data = mANQPCache.get(key); 122 if (data == null || data.expired()) { 123 mANQPCache.put(key, new ANQPData(network, null)); 124 return true; 125 } 126 else { 127 Log.d(Utils.hs2LogTag(getClass()), 128 String.format("BSSID %012x already in cache: %s", network.getBSSID(), data)); 129 return false; 130 } 131 } 132 } 133 134 public void update(NetworkDetail network, 135 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 136 137 CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 138 139 // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just 140 // long enough to prevent excessive re-querying. 141 synchronized (mANQPCache) { 142 ANQPData data = new ANQPData(network, anqpElements); 143 mANQPCache.put(key, data); 144 } 145 } 146 147 public ANQPData getEntry(NetworkDetail network) { 148 ANQPData data; 149 150 CacheKey key = CacheKey.buildKey(network, STANDARD_ESS); 151 synchronized (mANQPCache) { 152 data = mANQPCache.get(key); 153 } 154 155 return data != null && data.isValid(network) ? data : null; 156 } 157 158 public void clear(boolean all, boolean debug) { 159 long now = System.currentTimeMillis(); 160 synchronized (mANQPCache) { 161 if (all) { 162 mANQPCache.clear(); 163 mLastSweep = now; 164 } 165 else if (now > mLastSweep + CACHE_RECHECK) { 166 List<CacheKey> retirees = new ArrayList<>(); 167 for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) { 168 if (entry.getValue().expired(now)) { 169 retirees.add(entry.getKey()); 170 } 171 } 172 for (CacheKey key : retirees) { 173 mANQPCache.remove(key); 174 if (debug) { 175 Log.d(Utils.hs2LogTag(getClass()), "Retired " + key); 176 } 177 } 178 mLastSweep = now; 179 } 180 } 181 } 182} 183