1package com.android.server.wifi.hotspot2; 2 3import com.android.server.wifi.Clock; 4import com.android.server.wifi.anqp.ANQPElement; 5import com.android.server.wifi.anqp.Constants; 6 7import java.util.ArrayList; 8import java.util.Collections; 9import java.util.HashMap; 10import java.util.List; 11import java.util.Map; 12 13public class ANQPData { 14 /** 15 * The regular cache time for entries with a non-zero domain id. 16 */ 17 private static final long ANQP_QUALIFIED_CACHE_TIMEOUT = 3600000L; 18 /** 19 * The cache time for entries with a zero domain id. The zero domain id indicates that ANQP 20 * data from the AP may change at any time, thus a relatively short cache time is given to 21 * such data, but still long enough to avoid excessive querying. 22 */ 23 private static final long ANQP_UNQUALIFIED_CACHE_TIMEOUT = 300000L; 24 /** 25 * This is the hold off time for pending queries, i.e. the time during which subsequent queries 26 * are squelched. 27 */ 28 private static final long ANQP_HOLDOFF_TIME = 10000L; 29 30 /** 31 * Max value for the retry counter for unanswered queries. This limits the maximum time-out to 32 * ANQP_HOLDOFF_TIME * 2^MAX_RETRY. With current values this results in 640s. 33 */ 34 private static final int MAX_RETRY = 6; 35 36 private final NetworkDetail mNetwork; 37 private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements; 38 private final long mCtime; 39 private final long mExpiry; 40 private final int mRetry; 41 private final Clock mClock; 42 43 public ANQPData(Clock clock, NetworkDetail network, 44 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 45 46 mClock = clock; 47 mNetwork = network; 48 mANQPElements = anqpElements != null ? new HashMap<>(anqpElements) : null; 49 mCtime = mClock.currentTimeMillis(); 50 mRetry = 0; 51 if (anqpElements == null) { 52 mExpiry = mCtime + ANQP_HOLDOFF_TIME; 53 } 54 else if (network.getAnqpDomainID() == 0) { 55 mExpiry = mCtime + ANQP_UNQUALIFIED_CACHE_TIMEOUT; 56 } 57 else { 58 mExpiry = mCtime + ANQP_QUALIFIED_CACHE_TIMEOUT; 59 } 60 } 61 62 public ANQPData(Clock clock, NetworkDetail network, ANQPData existing) { 63 mClock = clock; 64 mNetwork = network; 65 mANQPElements = null; 66 mCtime = mClock.currentTimeMillis(); 67 if (existing == null) { 68 mRetry = 0; 69 mExpiry = mCtime + ANQP_HOLDOFF_TIME; 70 } 71 else { 72 mRetry = Math.max(existing.getRetry() + 1, MAX_RETRY); 73 mExpiry = ANQP_HOLDOFF_TIME * (1<<mRetry); 74 } 75 } 76 77 public List<Constants.ANQPElementType> disjoint(List<Constants.ANQPElementType> querySet) { 78 if (mANQPElements == null) { 79 // Ignore the query set for pending responses, it has minimal probability to happen 80 // and a new query will be reissued on the next round anyway. 81 return null; 82 } 83 else { 84 List<Constants.ANQPElementType> additions = new ArrayList<>(); 85 for (Constants.ANQPElementType element : querySet) { 86 if (!mANQPElements.containsKey(element)) { 87 additions.add(element); 88 } 89 } 90 return additions.isEmpty() ? null : additions; 91 } 92 } 93 94 public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() { 95 return Collections.unmodifiableMap(mANQPElements); 96 } 97 98 public NetworkDetail getNetwork() { 99 return mNetwork; 100 } 101 102 public boolean expired() { 103 return expired(mClock.currentTimeMillis()); 104 } 105 106 public boolean expired(long at) { 107 return mExpiry <= at; 108 } 109 110 protected boolean hasData() { 111 return mANQPElements != null; 112 } 113 114 protected void merge(Map<Constants.ANQPElementType, ANQPElement> data) { 115 if (data != null) { 116 mANQPElements.putAll(data); 117 } 118 } 119 120 protected boolean isValid(NetworkDetail nwk) { 121 return mANQPElements != null && 122 nwk.getAnqpDomainID() == mNetwork.getAnqpDomainID() && 123 mExpiry > mClock.currentTimeMillis(); 124 } 125 126 private int getRetry() { 127 return mRetry; 128 } 129 130 public String toString(boolean brief) { 131 StringBuilder sb = new StringBuilder(); 132 sb.append(mNetwork.toKeyString()).append(", domid ").append(mNetwork.getAnqpDomainID()); 133 if (mANQPElements == null) { 134 sb.append(", unresolved, "); 135 } 136 else { 137 sb.append(", ").append(mANQPElements.size()).append(" elements, "); 138 } 139 long now = mClock.currentTimeMillis(); 140 sb.append(Utils.toHMS(now-mCtime)).append(" old, expires in "). 141 append(Utils.toHMS(mExpiry-now)).append(' '); 142 if (brief) { 143 sb.append(expired(now) ? 'x' : '-'); 144 sb.append(mANQPElements == null ? 'u' : '-'); 145 } 146 else if (mANQPElements != null) { 147 sb.append(" data=").append(mANQPElements); 148 } 149 return sb.toString(); 150 } 151 152 @Override 153 public String toString() { 154 return toString(true); 155 } 156} 157