summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2016-12-14 03:29:12 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2016-12-14 03:29:14 +0000
commit79c9eb4cfa9b7a45fc4f26ec2ed304f5b76fb9ff (patch)
treee9e1a2ab965c9c4403de4ac0aeef7fcc96d92131 /service
parent227865949b847b2598867947b96e63ee6a04860c (diff)
parentf1b7517b04fedc6fd81f34a8cb84ce583b8ea63e (diff)
Merge "hotspot2: simplify ANQP cache management"
Diffstat (limited to 'service')
-rw-r--r--service/java/com/android/server/wifi/hotspot2/ANQPData.java169
-rw-r--r--service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java105
-rw-r--r--service/java/com/android/server/wifi/hotspot2/AnqpCache.java230
3 files changed, 218 insertions, 286 deletions
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPData.java b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
index 9f04c872c..7a708c5dd 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPData.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
@@ -1,156 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.wifi.hotspot2;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.Clock;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+/**
+ * Class for maintaining ANQP elements and managing the lifetime of the elements.
+ */
public class ANQPData {
/**
- * The regular cache time for entries with a non-zero domain id.
- */
- private static final long ANQP_QUALIFIED_CACHE_TIMEOUT = 3600000L;
- /**
- * The cache time for entries with a zero domain id. The zero domain id indicates that ANQP
- * data from the AP may change at any time, thus a relatively short cache time is given to
- * such data, but still long enough to avoid excessive querying.
+ * Entry lifetime.
*/
- private static final long ANQP_UNQUALIFIED_CACHE_TIMEOUT = 300000L;
- /**
- * This is the hold off time for pending queries, i.e. the time during which subsequent queries
- * are squelched.
- */
- private static final long ANQP_HOLDOFF_TIME = 10000L;
+ @VisibleForTesting
+ public static final long DATA_LIFETIME_MILLISECONDS = 3600000L;
- /**
- * Max value for the retry counter for unanswered queries. This limits the maximum time-out to
- * ANQP_HOLDOFF_TIME * 2^MAX_RETRY. With current values this results in 640s.
- */
- private static final int MAX_RETRY = 6;
-
- private final NetworkDetail mNetwork;
- private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
- private final long mCtime;
- private final long mExpiry;
- private final int mRetry;
private final Clock mClock;
+ private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
+ private final long mExpiryTime;
- public ANQPData(Clock clock, NetworkDetail network,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
-
- mClock = clock;
- mNetwork = network;
- mANQPElements = anqpElements != null ? new HashMap<>(anqpElements) : null;
- mCtime = mClock.getWallClockMillis();
- mRetry = 0;
- if (anqpElements == null) {
- mExpiry = mCtime + ANQP_HOLDOFF_TIME;
- }
- else if (network.getAnqpDomainID() == 0) {
- mExpiry = mCtime + ANQP_UNQUALIFIED_CACHE_TIMEOUT;
- }
- else {
- mExpiry = mCtime + ANQP_QUALIFIED_CACHE_TIMEOUT;
- }
- }
-
- public ANQPData(Clock clock, NetworkDetail network, ANQPData existing) {
+ public ANQPData(Clock clock, Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
mClock = clock;
- mNetwork = network;
- mANQPElements = null;
- mCtime = mClock.getWallClockMillis();
- if (existing == null) {
- mRetry = 0;
- mExpiry = mCtime + ANQP_HOLDOFF_TIME;
- }
- else {
- mRetry = Math.max(existing.getRetry() + 1, MAX_RETRY);
- mExpiry = ANQP_HOLDOFF_TIME * (1<<mRetry);
- }
- }
-
- public List<Constants.ANQPElementType> disjoint(List<Constants.ANQPElementType> querySet) {
- if (mANQPElements == null) {
- // Ignore the query set for pending responses, it has minimal probability to happen
- // and a new query will be reissued on the next round anyway.
- return null;
- }
- else {
- List<Constants.ANQPElementType> additions = new ArrayList<>();
- for (Constants.ANQPElementType element : querySet) {
- if (!mANQPElements.containsKey(element)) {
- additions.add(element);
- }
- }
- return additions.isEmpty() ? null : additions;
+ mANQPElements = new HashMap<>();
+ if (anqpElements != null) {
+ mANQPElements.putAll(anqpElements);
}
+ mExpiryTime = mClock.getElapsedSinceBootMillis() + DATA_LIFETIME_MILLISECONDS;
}
- public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
+ /**
+ * Return the ANQP elements.
+ *
+ * @return Map of ANQP elements
+ */
+ public Map<Constants.ANQPElementType, ANQPElement> getElements() {
return Collections.unmodifiableMap(mANQPElements);
}
- public NetworkDetail getNetwork() {
- return mNetwork;
- }
-
- public boolean expired() {
- return expired(mClock.getWallClockMillis());
- }
-
+ /**
+ * Check if this entry is expired at the specified time.
+ *
+ * @param at The time to check for
+ * @return true if it is expired at the given time
+ */
public boolean expired(long at) {
- return mExpiry <= at;
- }
-
- protected boolean hasData() {
- return mANQPElements != null;
- }
-
- protected void merge(Map<Constants.ANQPElementType, ANQPElement> data) {
- if (data != null) {
- mANQPElements.putAll(data);
- }
+ return mExpiryTime <= at;
}
- protected boolean isValid(NetworkDetail nwk) {
- return mANQPElements != null &&
- nwk.getAnqpDomainID() == mNetwork.getAnqpDomainID() &&
- mExpiry > mClock.getWallClockMillis();
- }
-
- private int getRetry() {
- return mRetry;
- }
-
- public String toString(boolean brief) {
+ @Override
+ public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append(mNetwork.toKeyString()).append(", domid ").append(mNetwork.getAnqpDomainID());
if (mANQPElements == null) {
sb.append(", unresolved, ");
}
else {
sb.append(", ").append(mANQPElements.size()).append(" elements, ");
}
- long now = mClock.getWallClockMillis();
- sb.append(Utils.toHMS(now-mCtime)).append(" old, expires in ").
- append(Utils.toHMS(mExpiry-now)).append(' ');
- if (brief) {
- sb.append(expired(now) ? 'x' : '-');
- sb.append(mANQPElements == null ? 'u' : '-');
- }
- else if (mANQPElements != null) {
- sb.append(" data=").append(mANQPElements);
- }
+ long now = mClock.getElapsedSinceBootMillis();
+ sb.append(" expires in ").append(Utils.toHMS(mExpiryTime - now)).append(' ');
+ sb.append(expired(now) ? 'x' : '-');
+ sb.append(mANQPElements == null ? 'u' : '-');
return sb.toString();
}
-
- @Override
- public String toString() {
- return toString(true);
- }
}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
new file mode 100644
index 000000000..aaaedb37d
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.text.TextUtils;
+
+/**
+ * Unique key for identifying APs that will contain the same ANQP information.
+ *
+ * APs in the same ESS (SSID or HESSID) with the same ANQP domain ID will have the same ANQP
+ * information. Thus, those APs will be keyed by the ESS identifier (SSID or HESSID) and the
+ * ANQP domain ID.
+ *
+ * APs without ANQP domain ID set will assumed to have unique ANQP information. Thus, those
+ * APs will be keyed by SSID and BSSID.
+ */
+public class ANQPNetworkKey {
+ private final String mSSID;
+ private final long mBSSID;
+ private final long mHESSID;
+ private final int mAnqpDomainID;
+
+ public ANQPNetworkKey(String ssid, long bssid, long hessid, int anqpDomainID) {
+ mSSID = ssid;
+ mBSSID = bssid;
+ mHESSID = hessid;
+ mAnqpDomainID = anqpDomainID;
+ }
+
+ /**
+ * Build an ANQP network key suitable for the granularity of the key space as follows:
+ *
+ * HESSID domainID Key content Rationale
+ * -------- ----------- ----------- --------------------
+ * n/a zero SSID/BSSID Domain ID indicates unique AP info
+ * not set set SSID/domainID Standard definition of an ESS
+ * set set HESSID/domainID The ESS is defined by the HESSID
+ *
+ * @param ssid The SSID of the AP
+ * @param bssid The BSSID of the AP
+ * @param hessid The HESSID of the AP
+ * @param anqpDomainId The ANQP Domain ID of the AP
+ * @return {@link ANQPNetworkKey}
+ */
+ public static ANQPNetworkKey buildKey(String ssid, long bssid, long hessid, int anqpDomainId) {
+ if (anqpDomainId == 0) {
+ return new ANQPNetworkKey(ssid, bssid, 0, 0);
+ } else if (hessid != 0L) {
+ return new ANQPNetworkKey(null, 0, hessid, anqpDomainId);
+ }
+ return new ANQPNetworkKey(ssid, 0, 0, anqpDomainId);
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHESSID != 0) {
+ return (int) (((mHESSID >>> 32) * 31 + mHESSID) * 31 + mAnqpDomainID);
+ } else if (mBSSID != 0) {
+ return (int) ((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
+ } else {
+ return mSSID.hashCode() * 31 + mAnqpDomainID;
+ }
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof ANQPNetworkKey)) {
+ return false;
+ }
+ ANQPNetworkKey that = (ANQPNetworkKey) thatObject;
+ return TextUtils.equals(that.mSSID, mSSID)
+ && that.mBSSID == mBSSID
+ && that.mHESSID == mHESSID
+ && that.mAnqpDomainID == mAnqpDomainID;
+ }
+
+ @Override
+ public String toString() {
+ if (mHESSID != 0L) {
+ return Utils.macToString(mHESSID) + ":" + mAnqpDomainID;
+ } else if (mBSSID != 0L) {
+ return Utils.macToString(mBSSID)
+ + ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
+ } else {
+ return "<" + Utils.toUnicodeEscapedString(mSSID) + ">:" + mAnqpDomainID;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
index 06e504ed4..ba96cff39 100644
--- a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
+++ b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
@@ -1,7 +1,22 @@
-package com.android.server.wifi.hotspot2;
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-import android.util.Log;
+package com.android.server.wifi.hotspot2;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.Clock;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants;
@@ -12,194 +27,77 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+/**
+ * Cache for storing ANQP data. This is simply a data cache, all the logic related to
+ * ANQP data query will be handled elsewhere (e.g. the consumer of the cache).
+ */
public class AnqpCache {
- private static final boolean DBG = false;
+ @VisibleForTesting
+ public static final long CACHE_SWEEP_INTERVAL_MILLISECONDS = 60000L;
- private static final long CACHE_RECHECK = 60000L;
- private static final boolean STANDARD_ESS = true; // Regular AP keying; see CacheKey below.
private long mLastSweep;
private Clock mClock;
- private final HashMap<CacheKey, ANQPData> mANQPCache;
+ private final Map<ANQPNetworkKey, ANQPData> mANQPCache;
public AnqpCache(Clock clock) {
mClock = clock;
mANQPCache = new HashMap<>();
- mLastSweep = mClock.getWallClockMillis();
+ mLastSweep = mClock.getElapsedSinceBootMillis();
}
- private static class CacheKey {
- private final String mSSID;
- private final long mBSSID;
- private final long mHESSID;
-
- private CacheKey(String ssid, long bssid, long hessid) {
- mSSID = ssid;
- mBSSID = bssid;
- mHESSID = hessid;
- }
-
- /**
- * Build an ANQP cache key suitable for the granularity of the key space as follows:
- *
- * HESSID domainID standardESS Key content Rationale
- * -------- ----------- --------------- ----------- --------------------
- * n/a zero n/a SSID/BSSID Domain ID indicates unique AP info
- * not set set false SSID/BSSID Strict per AP keying override
- * not set set true SSID Standard definition of an ESS
- * set set n/a HESSID The ESS is defined by the HESSID
- *
- * @param network The network to build the key for.
- * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used
- * for the cache, i.e. all APs with identical SSID is considered an ESS,
- * otherwise caching is performed per AP.
- * @return A CacheKey.
- */
- private static CacheKey buildKey(NetworkDetail network, boolean standardESS) {
- String ssid;
- long bssid;
- long hessid;
- if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) {
- ssid = network.getSSID();
- bssid = network.getBSSID();
- hessid = 0L;
- }
- else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) {
- ssid = null;
- bssid = 0L;
- hessid = network.getHESSID();
- }
- else {
- ssid = network.getSSID();
- bssid = 0L;
- hessid = 0L;
- }
-
- return new CacheKey(ssid, bssid, hessid);
- }
-
- @Override
- public int hashCode() {
- if (mHESSID != 0) {
- return (int)((mHESSID >>> 32) * 31 + mHESSID);
- }
- else if (mBSSID != 0) {
- return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
- }
- else {
- return mSSID.hashCode();
- }
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- }
- else if (thatObject == null || thatObject.getClass() != CacheKey.class) {
- return false;
- }
- CacheKey that = (CacheKey) thatObject;
- return Utils.compare(that.mSSID, mSSID) == 0 &&
- that.mBSSID == mBSSID &&
- that.mHESSID == mHESSID;
- }
-
- @Override
- public String toString() {
- if (mHESSID != 0L) {
- return "HESSID:" + NetworkDetail.toMACString(mHESSID);
- }
- else if (mBSSID != 0L) {
- return NetworkDetail.toMACString(mBSSID) +
- ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
- }
- else {
- return '<' + Utils.toUnicodeEscapedString(mSSID) + '>';
- }
- }
+ /**
+ * Add an ANQP entry associated with the given key.
+ *
+ * @param key The key that's associated with the entry
+ * @param anqpElements The ANQP elements from the AP
+ */
+ public void addEntry(ANQPNetworkKey key,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ ANQPData data = new ANQPData(mClock, anqpElements);
+ mANQPCache.put(key, data);
}
- public List<Constants.ANQPElementType> initiate(NetworkDetail network,
- List<Constants.ANQPElementType> querySet) {
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
-
- synchronized (mANQPCache) {
- ANQPData data = mANQPCache.get(key);
- if (data == null || data.expired()) {
- mANQPCache.put(key, new ANQPData(mClock, network, data));
- return querySet;
- }
- else {
- List<Constants.ANQPElementType> newList = data.disjoint(querySet);
- Log.d(Utils.hs2LogTag(getClass()),
- String.format("New ANQP elements for BSSID %012x: %s",
- network.getBSSID(), newList));
- return newList;
- }
- }
+ /**
+ * Get the ANQP data associated with the given AP.
+ *
+ * @param key The key that's associated with the entry
+ * @return {@link ANQPData}
+ */
+ public ANQPData getEntry(ANQPNetworkKey key) {
+ return mANQPCache.get(key);
}
- public void update(NetworkDetail network,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
-
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
-
- // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just
- // long enough to prevent excessive re-querying.
- synchronized (mANQPCache) {
- ANQPData data = mANQPCache.get(key);
- if (data != null && data.hasData()) {
- data.merge(anqpElements);
- }
- else {
- data = new ANQPData(mClock, network, anqpElements);
- mANQPCache.put(key, data);
- }
+ /**
+ * Go through the cache to remove any expired entries.
+ */
+ public void sweep() {
+ long now = mClock.getElapsedSinceBootMillis();
+ // Check if it is time to perform the sweep.
+ if (now < mLastSweep + CACHE_SWEEP_INTERVAL_MILLISECONDS) {
+ return;
}
- }
- public ANQPData getEntry(NetworkDetail network) {
- ANQPData data;
-
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
- synchronized (mANQPCache) {
- data = mANQPCache.get(key);
+ // Get all expired keys.
+ List<ANQPNetworkKey> expiredKeys = new ArrayList<>();
+ for (Map.Entry<ANQPNetworkKey, ANQPData> entry : mANQPCache.entrySet()) {
+ if (entry.getValue().expired(now)) {
+ expiredKeys.add(entry.getKey());
+ }
}
- return data != null && data.isValid(network) ? data : null;
- }
-
- public void clear(boolean all, boolean debug) {
- if (DBG) Log.d(Utils.hs2LogTag(getClass()), "Clearing ANQP cache: all: " + all);
- long now = mClock.getWallClockMillis();
- synchronized (mANQPCache) {
- if (all) {
- mANQPCache.clear();
- mLastSweep = now;
- }
- else if (now > mLastSweep + CACHE_RECHECK) {
- List<CacheKey> retirees = new ArrayList<>();
- for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) {
- if (entry.getValue().expired(now)) {
- retirees.add(entry.getKey());
- }
- }
- for (CacheKey key : retirees) {
- mANQPCache.remove(key);
- if (debug) {
- Log.d(Utils.hs2LogTag(getClass()), "Retired " + key);
- }
- }
- mLastSweep = now;
- }
+ // Remove all expired entries.
+ for (ANQPNetworkKey key : expiredKeys) {
+ mANQPCache.remove(key);
}
+ mLastSweep = now;
}
public void dump(PrintWriter out) {
- out.println("Last sweep " + Utils.toHMS(mClock.getWallClockMillis() - mLastSweep) + " ago.");
+ out.println("Last sweep " + Utils.toHMS(mClock.getElapsedSinceBootMillis() - mLastSweep)
+ + " ago.");
for (ANQPData anqpData : mANQPCache.values()) {
- out.println(anqpData.toString(false));
+ out.println(anqpData);
}
}
}