1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage com.android.hotspot2.osu;
2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.util.Log;
4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.anqp.HSIconFileElement;
6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.anqp.IconInfo;
7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.Utils;
8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.ProtocolException;
10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.BufferUnderflowException;
11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.ByteBuffer;
12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.ByteOrder;
13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.ArrayList;
14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Arrays;
15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Collections;
16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashMap;
17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Iterator;
18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.LinkedHashMap;
19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.LinkedList;
20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.List;
21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Map;
22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport static com.android.anqp.Constants.ANQPElementType.HSIconFile;
24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic class IconCache extends Thread {
26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static final int CacheSize = 64;
27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static final int RetryCount = 3;
28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private final OSUManager mOSUManager;
30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private final Map<Long, LinkedList<QuerySet>> mBssQueues = new HashMap<>();
31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private final Map<IconKey, HSIconFileElement> mCache =
33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            new LinkedHashMap<IconKey, HSIconFileElement>() {
34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                @Override
35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                protected boolean removeEldestEntry(Map.Entry eldest) {
36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    return size() > CacheSize;
37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            };
39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static class IconKey {
41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final long mBSSID;
42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final long mHESSID;
43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final String mSSID;
44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final int mAnqpDomID;
45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final String mFileName;
46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private IconKey(OSUInfo osuInfo, String fileName) {
48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mBSSID = osuInfo.getBSSID();
49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mHESSID = osuInfo.getHESSID();
50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mSSID = osuInfo.getAdvertisingSSID();
51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mAnqpDomID = osuInfo.getAnqpDomID();
52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mFileName = fileName;
53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public String getFileName() {
56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mFileName;
57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public boolean equals(Object thatObject) {
61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (this == thatObject) {
62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return true;
63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (thatObject == null || getClass() != thatObject.getClass()) {
65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return false;
66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            IconKey that = (IconKey) thatObject;
69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mFileName.equals(that.mFileName) && ((mBSSID == that.mBSSID) ||
71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    ((mAnqpDomID == that.mAnqpDomID) && (mAnqpDomID != 0) &&
72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            (mHESSID == that.mHESSID) && ((mHESSID != 0)
73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            || mSSID.equals(that.mSSID))));
74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public int hashCode() {
78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            int result = (int) (mBSSID ^ (mBSSID >>> 32));
79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            result = 31 * result + (int) (mHESSID ^ (mHESSID >>> 32));
80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            result = 31 * result + mSSID.hashCode();
81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            result = 31 * result + mAnqpDomID;
82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            result = 31 * result + mFileName.hashCode();
83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return result;
84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public String toString() {
88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return String.format("%012x:%012x '%s' [%d] + '%s'",
89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    mBSSID, mHESSID, mSSID, mAnqpDomID, mFileName);
90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static class QueryEntry {
94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final IconKey mKey;
95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private int mRetry;
96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private long mLastSent;
97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private QueryEntry(IconKey key) {
99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mKey = key;
100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mLastSent = System.currentTimeMillis();
101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private IconKey getKey() {
104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mKey;
105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private int bumpRetry() {
108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mLastSent = System.currentTimeMillis();
109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mRetry++;
110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private long age(long now) {
113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return now - mLastSent;
114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public String toString() {
118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return String.format("Entry %s, retry %d", mKey, mRetry);
119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static class QuerySet {
123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final OSUInfo mOsuInfo;
124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private final LinkedList<QueryEntry> mEntries;
125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private QuerySet(OSUInfo osuInfo, List<IconInfo> icons) {
127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mOsuInfo = osuInfo;
128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mEntries = new LinkedList<>();
129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            for (IconInfo iconInfo : icons) {
130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mEntries.addLast(new QueryEntry(new IconKey(osuInfo, iconInfo.getFileName())));
131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private QueryEntry peek() {
135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mEntries.getFirst();
136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private QueryEntry pop() {
139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mEntries.removeFirst();
140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mEntries.isEmpty() ? null : mEntries.getFirst();
141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private boolean isEmpty() {
144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mEntries.isEmpty();
145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private List<QueryEntry> getAllEntries() {
148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return Collections.unmodifiableList(mEntries);
149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private long getBssid() {
152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mOsuInfo.getBSSID();
153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private OSUInfo getOsuInfo() {
156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mOsuInfo;
157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private IconKey updateIcon(String fileName, HSIconFileElement iconFileElement) {
160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            IconKey key = null;
161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            for (QueryEntry queryEntry : mEntries) {
162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                if (queryEntry.getKey().getFileName().equals(fileName)) {
163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    key = queryEntry.getKey();
164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (key == null) {
167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return null;
168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (iconFileElement != null) {
171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mOsuInfo.setIconFileElement(iconFileElement, fileName);
172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            } else {
173ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mOsuInfo.setIconStatus(OSUInfo.IconStatus.NotAvailable);
174ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
175ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return key;
176ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
177ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
178ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private boolean updateIcon(IconKey key, HSIconFileElement iconFileElement) {
179ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            boolean match = false;
180ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            for (QueryEntry queryEntry : mEntries) {
181ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                if (queryEntry.getKey().equals(key)) {
182ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    match = true;
183ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    break;
184ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
185ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
186ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (!match) {
187ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return false;
188ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
189ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
190ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (iconFileElement != null) {
191ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mOsuInfo.setIconFileElement(iconFileElement, key.getFileName());
192ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            } else {
193ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mOsuInfo.setIconStatus(OSUInfo.IconStatus.NotAvailable);
194ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
195ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return true;
196ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
197ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
198ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
199ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public String toString() {
200ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return "OSU " + mOsuInfo + ": " + mEntries;
201ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
202ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
203ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
204ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public IconCache(OSUManager osuManager) {
205ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        mOSUManager = osuManager;
206ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
207ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
208ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void clear() {
209ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        mBssQueues.clear();
210ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        mCache.clear();
211ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
212ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
213ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private boolean enqueue(QuerySet querySet) {
214ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        boolean newEntry = false;
215ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        LinkedList<QuerySet> queries = mBssQueues.get(querySet.getBssid());
216ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (queries == null) {
217ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            queries = new LinkedList<>();
218ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mBssQueues.put(querySet.getBssid(), queries);
219ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            newEntry = true;
220ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
221ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        queries.addLast(querySet);
222ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        return newEntry;
223ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
224ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
225ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void startIconQuery(OSUInfo osuInfo, List<IconInfo> icons) {
226ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        Log.d("ZXZ", String.format("Icon query on %012x for %s", osuInfo.getBSSID(), icons));
227ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (icons == null || icons.isEmpty()) {
228ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return;
229ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
230ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
231ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        QuerySet querySet = new QuerySet(osuInfo, icons);
232ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        for (QueryEntry entry : querySet.getAllEntries()) {
233ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            HSIconFileElement iconElement = mCache.get(entry.getKey());
234ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (iconElement != null) {
235ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                osuInfo.setIconFileElement(iconElement, entry.getKey().getFileName());
236ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mOSUManager.iconResults(Arrays.asList(osuInfo));
237ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return;
238ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
239ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
240ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (enqueue(querySet)) {
241ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            initiateQuery(querySet.getBssid());
242ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
243ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
244ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
245ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private void initiateQuery(long bssid) {
246ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        LinkedList<QuerySet> queryEntries = mBssQueues.get(bssid);
247ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (queryEntries == null) {
248ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return;
249ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        } else if (queryEntries.isEmpty()) {
250ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mBssQueues.remove(bssid);
251ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return;
252ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
253ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
254ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        QuerySet querySet = queryEntries.getFirst();
255ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        QueryEntry queryEntry = querySet.peek();
256ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (queryEntry.bumpRetry() >= RetryCount) {
257ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            QueryEntry newEntry = querySet.pop();
258ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (newEntry == null) {
259ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                // No more entries in this QuerySet, advance to the next set.
260ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                querySet.getOsuInfo().setIconStatus(OSUInfo.IconStatus.NotAvailable);
261ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                queryEntries.removeFirst();
262ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                if (queryEntries.isEmpty()) {
263ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    // No further QuerySet on this BSSID, drop the bucket and bail.
264ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    mBssQueues.remove(bssid);
265ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    return;
266ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                } else {
267ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    querySet = queryEntries.getFirst();
268ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    queryEntry = querySet.peek();
269ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    queryEntry.bumpRetry();
270ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
271ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
272ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
273ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        mOSUManager.doIconQuery(bssid, queryEntry.getKey().getFileName());
274ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
275ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
276ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void notifyIconReceived(long bssid, String fileName, byte[] iconData) {
277ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        Log.d("ZXZ", String.format("Icon '%s':%d received from %012x",
278ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                fileName, iconData != null ? iconData.length : -1, bssid));
279ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        IconKey key;
280ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        HSIconFileElement iconFileElement = null;
281ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        List<OSUInfo> updates = new ArrayList<>();
282ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
283ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        LinkedList<QuerySet> querySets = mBssQueues.get(bssid);
284ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (querySets == null || querySets.isEmpty()) {
285ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            Log.d(OSUManager.TAG,
286ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    String.format("Spurious icon response from %012x for '%s' (%d) bytes",
287ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            bssid, fileName, iconData != null ? iconData.length : -1));
288ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            Log.d("ZXZ", "query set: " + querySets
289ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    + ", BSS queues: " + Utils.bssidsToString(mBssQueues.keySet()));
290ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return;
291ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        } else {
292ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            QuerySet querySet = querySets.removeFirst();
293ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (iconData != null) {
294ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                try {
295ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    iconFileElement = new HSIconFileElement(HSIconFile,
296ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            ByteBuffer.wrap(iconData).order(ByteOrder.LITTLE_ENDIAN));
297ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                } catch (ProtocolException | BufferUnderflowException e) {
298ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    Log.e(OSUManager.TAG, "Failed to parse ANQP icon file: " + e);
299ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
300ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
301ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            key = querySet.updateIcon(fileName, iconFileElement);
302ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (key == null) {
303ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                Log.d(OSUManager.TAG,
304ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        String.format("Spurious icon response from %012x for '%s' (%d) bytes",
305ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                bssid, fileName, iconData != null ? iconData.length : -1));
306ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                Log.d("ZXZ", "query set: " + querySets + ", BSS queues: "
307ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        + Utils.bssidsToString(mBssQueues.keySet()));
308ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                querySets.addFirst(querySet);
309ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return;
310ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
311ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
312ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (iconFileElement != null) {
313ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mCache.put(key, iconFileElement);
314ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
315ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
316ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (querySet.isEmpty()) {
317ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mBssQueues.remove(bssid);
318ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
319ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            updates.add(querySet.getOsuInfo());
320ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
321ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
322ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        // Update any other pending entries that matches the ESS of the currently resolved icon
323ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        Iterator<Map.Entry<Long, LinkedList<QuerySet>>> bssIterator =
324ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mBssQueues.entrySet().iterator();
325ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        while (bssIterator.hasNext()) {
326ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            Map.Entry<Long, LinkedList<QuerySet>> bssEntries = bssIterator.next();
327ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            Iterator<QuerySet> querySetIterator = bssEntries.getValue().iterator();
328ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            while (querySetIterator.hasNext()) {
329ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                QuerySet querySet = querySetIterator.next();
330ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                if (querySet.updateIcon(key, iconFileElement)) {
331ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    querySetIterator.remove();
332ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    updates.add(querySet.getOsuInfo());
333ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
334ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
335ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (bssEntries.getValue().isEmpty()) {
336ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                bssIterator.remove();
337ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
338ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
339ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
340ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        initiateQuery(bssid);
341ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
342ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        mOSUManager.iconResults(updates);
343ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
344ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
345ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static final long RequeryTimeLow = 6000L;
346ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private static final long RequeryTimeHigh = 15000L;
347ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
348ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void tickle(boolean wifiOff) {
349ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        synchronized (mCache) {
350ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (wifiOff) {
351ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mBssQueues.clear();
352ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            } else {
353ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                long now = System.currentTimeMillis();
354ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
355ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                Iterator<Map.Entry<Long, LinkedList<QuerySet>>> bssIterator =
356ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        mBssQueues.entrySet().iterator();
357ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                while (bssIterator.hasNext()) {
358ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    // Get the list of entries for this BSSID
359ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    Map.Entry<Long, LinkedList<QuerySet>> bssEntries = bssIterator.next();
360ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    Iterator<QuerySet> querySetIterator = bssEntries.getValue().iterator();
361ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    while (querySetIterator.hasNext()) {
362ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        QuerySet querySet = querySetIterator.next();
363ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        QueryEntry queryEntry = querySet.peek();
364ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        long age = queryEntry.age(now);
365ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        if (age > RequeryTimeHigh) {
366ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            // Timed out entry, move on to the next.
367ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            queryEntry = querySet.pop();
368ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            if (queryEntry == null) {
369ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                // Empty query set, update status and remove it.
370ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                querySet.getOsuInfo()
371ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                        .setIconStatus(OSUInfo.IconStatus.NotAvailable);
372ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                querySetIterator.remove();
373ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            } else {
374ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                // Start a query on the next entry and bail out of the set iteration
375ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                initiateQuery(querySet.getBssid());
376ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                                break;
377ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            }
378ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        } else if (age > RequeryTimeLow) {
379ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            // Re-issue queries for qualified entries and bail out of set iteration
380ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            initiateQuery(querySet.getBssid());
381ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                            break;
382ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        }
383ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    }
384ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    if (bssEntries.getValue().isEmpty()) {
385ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        // Kill the whole bucket if the set list is empty
386ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                        bssIterator.remove();
387ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    }
388ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
389ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
390ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
391ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
392ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist}
393