1751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistpackage com.android.server.wifi.hotspot2;
2751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
3751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport android.util.Log;
4751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
5ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpandeimport com.android.server.wifi.Clock;
6751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport com.android.server.wifi.anqp.ANQPElement;
7751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport com.android.server.wifi.anqp.Constants;
8751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
99f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvistimport java.io.PrintWriter;
10751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.ArrayList;
11751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.HashMap;
12751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.List;
13751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvistimport java.util.Map;
14751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
15c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvistpublic class AnqpCache {
16af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato    private static final boolean DBG = false;
17af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato
18751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    private static final long CACHE_RECHECK = 60000L;
19a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist    private static final boolean STANDARD_ESS = true;  // Regular AP keying; see CacheKey below.
20c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist    private long mLastSweep;
21ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande    private Clock mClock;
22751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
23ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    private final HashMap<CacheKey, ANQPData> mANQPCache;
24751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
25ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande    public AnqpCache(Clock clock) {
26ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande        mClock = clock;
27751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        mANQPCache = new HashMap<>();
28ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande        mLastSweep = mClock.currentTimeMillis();
29751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    }
30751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
31ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    private static class CacheKey {
32ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        private final String mSSID;
33ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        private final long mBSSID;
34ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        private final long mHESSID;
35ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
36ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        private CacheKey(String ssid, long bssid, long hessid) {
37ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            mSSID = ssid;
38ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            mBSSID = bssid;
39ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            mHESSID = hessid;
40ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
41ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
42ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        /**
43ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * Build an ANQP cache key suitable for the granularity of the key space as follows:
44ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         *
45ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * HESSID   domainID    standardESS     Key content Rationale
46ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * -------- ----------- --------------- ----------- --------------------
47ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * n/a      zero        n/a             SSID/BSSID  Domain ID indicates unique AP info
48ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * not set  set         false           SSID/BSSID  Strict per AP keying override
49ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * not set  set         true            SSID        Standard definition of an ESS
50ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * set      set         n/a             HESSID      The ESS is defined by the HESSID
51ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         *
52ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * @param network The network to build the key for.
53ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used
54ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         *                    for the cache, i.e. all APs with identical SSID is considered an ESS,
55ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         *                    otherwise caching is performed per AP.
56ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         * @return A CacheKey.
57ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist         */
58ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        private static CacheKey buildKey(NetworkDetail network, boolean standardESS) {
59ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            String ssid;
60ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            long bssid;
61ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            long hessid;
62ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) {
63ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                ssid = network.getSSID();
64ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                bssid = network.getBSSID();
65ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                hessid = 0L;
66ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
67ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) {
68ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                ssid = null;
69ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                bssid = 0L;
70ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                hessid = network.getHESSID();
71ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
72ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else {
73ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                ssid = network.getSSID();
74ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                bssid = 0L;
75ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                hessid = 0L;
76ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
77ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
78ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            return new CacheKey(ssid, bssid, hessid);
79ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
80ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
81ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        @Override
82ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        public int hashCode() {
83ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            if (mHESSID != 0) {
84ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return (int)((mHESSID >>> 32) * 31 + mHESSID);
85ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
86ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else if (mBSSID != 0) {
87ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
88ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
89ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else {
90ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return mSSID.hashCode();
91ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
92ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
93ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
94ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        @Override
95ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        public boolean equals(Object thatObject) {
96ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            if (thatObject == this) {
97ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return true;
98ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
99ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else if (thatObject == null || thatObject.getClass() != CacheKey.class) {
100ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return false;
101ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
102ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            CacheKey that = (CacheKey) thatObject;
103ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            return Utils.compare(that.mSSID, mSSID) == 0 &&
104ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                    that.mBSSID == mBSSID &&
105ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                    that.mHESSID == mHESSID;
106ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
107ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
108ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        @Override
109ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        public String toString() {
110ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            if (mHESSID != 0L) {
111ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return "HESSID:" + NetworkDetail.toMACString(mHESSID);
112ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
113ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else if (mBSSID != 0L) {
114ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return NetworkDetail.toMACString(mBSSID) +
115ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                        ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
116ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
117ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            else {
118ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                return '<' + Utils.toUnicodeEscapedString(mSSID) + '>';
119ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            }
120ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
121ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    }
122ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
1231d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist    public List<Constants.ANQPElementType> initiate(NetworkDetail network,
1241d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                                                    List<Constants.ANQPElementType> querySet) {
125ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
126ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist
127751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        synchronized (mANQPCache) {
128ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            ANQPData data = mANQPCache.get(key);
1295bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist            if (data == null || data.expired()) {
130ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande                mANQPCache.put(key, new ANQPData(mClock, network, data));
1311d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                return querySet;
132751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist            }
133751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist            else {
1341d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                List<Constants.ANQPElementType> newList = data.disjoint(querySet);
1355bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist                Log.d(Utils.hs2LogTag(getClass()),
1361d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                        String.format("New ANQP elements for BSSID %012x: %s",
1371d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                                network.getBSSID(), newList));
1381d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                return newList;
139751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist            }
140751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        }
141751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    }
142751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
143751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    public void update(NetworkDetail network,
144751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist                       Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
145751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
146ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
147751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
148751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just
149751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        // long enough to prevent excessive re-querying.
150751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        synchronized (mANQPCache) {
1511d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist            ANQPData data = mANQPCache.get(key);
1521d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist            if (data != null && data.hasData()) {
1531d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                data.merge(anqpElements);
1541d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist            }
1551d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist            else {
156ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande                data = new ANQPData(mClock, network, anqpElements);
1571d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist                mANQPCache.put(key, data);
1581d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist            }
159751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        }
160751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    }
161751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
162751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    public ANQPData getEntry(NetworkDetail network) {
163751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        ANQPData data;
164751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
165ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
166751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        synchronized (mANQPCache) {
167ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            data = mANQPCache.get(key);
168751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        }
169751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
1705bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist        return data != null && data.isValid(network) ? data : null;
171751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    }
172751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist
173c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist    public void clear(boolean all, boolean debug) {
174af2b79e4cfc1da793b3d8fb4a96c144deefc7d58Joe Onorato        if (DBG) Log.d(Utils.hs2LogTag(getClass()), "Clearing ANQP cache: all: " + all);
175ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande        long now = mClock.currentTimeMillis();
176751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        synchronized (mANQPCache) {
177c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist            if (all) {
178c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                mANQPCache.clear();
179c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                mLastSweep = now;
180751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist            }
181c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist            else if (now > mLastSweep + CACHE_RECHECK) {
182c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                List<CacheKey> retirees = new ArrayList<>();
183c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) {
184c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                    if (entry.getValue().expired(now)) {
185c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                        retirees.add(entry.getKey());
186c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                    }
187c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                }
188c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                for (CacheKey key : retirees) {
189c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                    mANQPCache.remove(key);
190c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                    if (debug) {
191c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                        Log.d(Utils.hs2LogTag(getClass()), "Retired " + key);
192c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                    }
193c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                }
194c465290772b735d97cba451c9bc5b6cc76db9147Jan Nordqvist                mLastSweep = now;
195751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist            }
196751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist        }
197751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist    }
1989f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist
1999f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist    public void dump(PrintWriter out) {
200ae815bc71287f8a85727034c40bb07247a3d9415Vinit Deshpande        out.println("Last sweep " + Utils.toHMS(mClock.currentTimeMillis() - mLastSweep) + " ago.");
2019f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist        for (ANQPData anqpData : mANQPCache.values()) {
2029f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist            out.println(anqpData.toString(false));
2039f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist        }
2049f7795c694d048fdfecc4117cf5ddbd3e6ae1406Jan Nordqvist    }
205751c5a4496b6f90656eb24caa35f584d8976063dJan Nordqvist}
206