AnqpCache.java revision 751c5a4496b6f90656eb24caa35f584d8976063d
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
16    private final HashMap<NetworkDetail, ANQPData> mANQPCache;
17    private final HashMap<Long, ANQPData> mHESSIDCache;
18    private final Chronograph mChronograph;
19
20    public AnqpCache(Chronograph chronograph) {
21        mANQPCache = new HashMap<>();
22        mHESSIDCache = new HashMap<>();
23        mChronograph = chronograph;
24        mChronograph.addAlarm(CACHE_RECHECK, this, null);
25    }
26
27    public boolean initiate(NetworkDetail network) {
28        synchronized (mANQPCache) {
29            if (!mANQPCache.containsKey(network)) {
30                mANQPCache.put(network, new ANQPData(network, null));
31                return true;
32            }
33            else {
34                return false;
35            }
36        }
37    }
38
39    public int getRetry(NetworkDetail network) {
40        ANQPData data;
41        synchronized (mANQPCache) {
42            data = mANQPCache.get(network);
43        }
44        return data != null ? data.incrementAndGetRetry() : -1;
45    }
46
47    public void update(NetworkDetail network,
48                       Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
49
50        long now = System.currentTimeMillis();
51
52        // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just
53        // long enough to prevent excessive re-querying.
54        synchronized (mANQPCache) {
55            ANQPData data = mANQPCache.get(network);
56            if (data == null ||
57                    !data.isResolved() ||
58                    data.getDomainID() != network.getAnqpDomainID() ||
59                    data.recacheable(now)) {
60                data = new ANQPData(network, anqpElements);
61                mANQPCache.put(network, data);
62            }
63            // The spec really talks about caching per ESS, where an ESS is a set of APs with the
64            // same SSID. Since we're presumably in HS2.0 land here I have taken the liberty to
65            // tighten the definition of an ESS as the set of APs all sharing an HESSID.
66            if (network.getAnqpDomainID() != 0 && network.getHESSID() != 0 ) {
67                mHESSIDCache.put(network.getHESSID(), data);
68            }
69        }
70    }
71
72    public ANQPData getEntry(NetworkDetail network) {
73        ANQPData data;
74
75        synchronized (mANQPCache) {
76            data = mANQPCache.get(network);
77            if (data == null && network.getAnqpDomainID() != 0 && network.getHESSID() != 0) {
78                data = mHESSIDCache.get(network.getHESSID());
79            }
80        }
81
82        long now = System.currentTimeMillis();
83
84        if (data == null ||
85                !data.isResolved() ||
86                data.getDomainID() != network.getAnqpDomainID() ||
87                data.expired(now)) {
88            return null;
89        }
90        return data;
91    }
92
93    @Override
94    public void wake(Object token) {
95        long now = System.currentTimeMillis();
96        synchronized (mANQPCache) {
97            List<NetworkDetail> regulars = new ArrayList<>();
98            for (Map.Entry<NetworkDetail, ANQPData> entry : mANQPCache.entrySet()) {
99                if (entry.getValue().expired(now)) {
100                    regulars.add(entry.getKey());
101                }
102            }
103            for (NetworkDetail key : regulars) {
104                mANQPCache.remove(key);
105                Log.d("HS2J", "Retired " + key.toKeyString() + "/" + key.getAnqpDomainID());
106            }
107
108            List<Long> hessids = new ArrayList<>();
109            for (Map.Entry<Long, ANQPData> entry : mHESSIDCache.entrySet()) {
110                if (entry.getValue().expired(now)) {
111                    hessids.add(entry.getKey());
112                }
113            }
114            for (Long key : hessids) {
115                mANQPCache.remove(key);
116                Log.d("HS2J", "Retired HESSID " + key);
117            }
118        }
119        mChronograph.addAlarm(CACHE_RECHECK, this, null);
120    }
121}
122