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