ScanDetailCache.java revision cb59612d2af82044ee74e8f595c9a40498db14b4
1
2package com.android.server.wifi;
3
4import android.net.wifi.ScanResult;
5import android.net.wifi.WifiConfiguration;
6import android.util.Log;
7import android.os.SystemClock;
8
9import com.android.server.wifi.ScanDetail;
10import com.android.server.wifi.hotspot2.PasspointMatch;
11import com.android.server.wifi.hotspot2.PasspointMatchInfo;
12import com.android.server.wifi.hotspot2.pps.HomeSP;
13
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.Comparator;
18import java.util.HashMap;
19import java.util.Iterator;
20import java.util.List;
21
22class ScanDetailCache {
23
24    private static final String TAG = "ScanDetailCache";
25
26    private WifiConfiguration mConfig;
27    private HashMap<String, ScanDetail> mMap;
28    private HashMap<String, PasspointMatchInfo> mPasspointMatches;
29    private static boolean DBG=false;
30    ScanDetailCache(WifiConfiguration config) {
31        mConfig = config;
32        mMap = new HashMap();
33        mPasspointMatches = new HashMap();
34    }
35
36    void put(ScanDetail scanDetail) {
37        put(scanDetail, null, null);
38    }
39
40    void put(ScanDetail scanDetail, PasspointMatch match, HomeSP homeSp) {
41
42        mMap.put(scanDetail.getBSSIDString(), scanDetail);
43
44        if (match != null && homeSp != null) {
45            mPasspointMatches.put(scanDetail.getBSSIDString(),
46                    new PasspointMatchInfo(match, scanDetail, homeSp));
47        }
48    }
49
50    ScanResult get(String bssid) {
51        ScanDetail scanDetail = getScanDetail(bssid);
52        return scanDetail == null ? null : scanDetail.getScanResult();
53    }
54
55    ScanDetail getScanDetail(String bssid) {
56        return mMap.get(bssid);
57    }
58
59    void remove(String bssid) {
60        mMap.remove(bssid);
61    }
62
63    int size() {
64        return mMap.size();
65    }
66
67    boolean isEmpty() {
68        return size() == 0;
69    }
70
71    ScanDetail getFirst() {
72        Iterator<ScanDetail> it = mMap.values().iterator();
73        return it.hasNext() ? it.next() : null;
74    }
75
76    Collection<String> keySet() {
77        return mMap.keySet();
78    }
79
80    Collection<ScanDetail> values() {
81        return mMap.values();
82    }
83
84    public void trim(int num) {
85        int currentSize = mMap.size();
86        if (currentSize <= num) {
87            return; // Nothing to trim
88        }
89        ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
90        if (list.size() != 0) {
91            // Sort by descending timestamp
92            Collections.sort(list, new Comparator() {
93                public int compare(Object o1, Object o2) {
94                    ScanDetail a = (ScanDetail) o1;
95                    ScanDetail b = (ScanDetail) o2;
96                    if (a.getSeen() > b.getSeen()) {
97                        return 1;
98                    }
99                    if (a.getSeen() < b.getSeen()) {
100                        return -1;
101                    }
102                    return a.getBSSIDString().compareTo(b.getBSSIDString());
103                }
104            });
105        }
106        for (int i = 0; i < currentSize - num ; i++) {
107            // Remove oldest results from scan cache
108            ScanDetail result = list.get(i);
109            mMap.remove(result.getBSSIDString());
110            mPasspointMatches.remove(result.getBSSIDString());
111        }
112    }
113
114    /* @hide */
115    private ArrayList<ScanDetail> sort() {
116        ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
117        if (list.size() != 0) {
118            Collections.sort(list, new Comparator() {
119                public int compare(Object o1, Object o2) {
120                    ScanResult a = ((ScanDetail)o1).getScanResult();
121                    ScanResult b = ((ScanDetail)o2).getScanResult();
122                    if (a.numIpConfigFailures > b.numIpConfigFailures) {
123                        return 1;
124                    }
125                    if (a.numIpConfigFailures < b.numIpConfigFailures) {
126                        return -1;
127                    }
128                    if (a.seen > b.seen) {
129                        return -1;
130                    }
131                    if (a.seen < b.seen) {
132                        return 1;
133                    }
134                    if (a.level > b.level) {
135                        return -1;
136                    }
137                    if (a.level < b.level) {
138                        return 1;
139                    }
140                    return a.BSSID.compareTo(b.BSSID);
141                }
142            });
143        }
144        return list;
145    }
146
147    public WifiConfiguration.Visibility getVisibilityByRssi(long age) {
148        WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
149
150        long now_ms = System.currentTimeMillis();
151        long now_elapsed_ms = SystemClock.elapsedRealtime();
152        for(ScanDetail scanDetail : values()) {
153            ScanResult result = scanDetail.getScanResult();
154            if (scanDetail.getSeen() == 0)
155                continue;
156
157            if (result.is5GHz()) {
158                //strictly speaking: [4915, 5825]
159                //number of known BSSID on 5GHz band
160                status.num5 = status.num5 + 1;
161            } else if (result.is24GHz()) {
162                //strictly speaking: [2412, 2482]
163                //number of known BSSID on 2.4Ghz band
164                status.num24 = status.num24 + 1;
165            }
166
167            if (result.timestamp != 0) {
168                if (DBG) {
169                    Log.e("getVisibilityByRssi", " considering " + result.SSID + " " + result.BSSID
170                        + " elapsed=" + now_elapsed_ms + " timestamp=" + result.timestamp
171                        + " age = " + age);
172                }
173                if ((now_elapsed_ms - (result.timestamp/1000)) > age) continue;
174            } else {
175                // This check the time at which we have received the scan result from supplicant
176                if ((now_ms - result.seen) > age) continue;
177            }
178
179            if (result.is5GHz()) {
180                if (result.level > status.rssi5) {
181                    status.rssi5 = result.level;
182                    status.age5 = result.seen;
183                    status.BSSID5 = result.BSSID;
184                }
185            } else if (result.is24GHz()) {
186                if (result.level > status.rssi24) {
187                    status.rssi24 = result.level;
188                    status.age24 = result.seen;
189                    status.BSSID24 = result.BSSID;
190                }
191            }
192        }
193
194        return status;
195    }
196
197    public WifiConfiguration.Visibility getVisibilityByPasspointMatch(long age) {
198
199        long now_ms = System.currentTimeMillis();
200        PasspointMatchInfo pmiBest24 = null, pmiBest5 = null;
201
202        for(PasspointMatchInfo pmi : mPasspointMatches.values()) {
203            ScanDetail scanDetail = pmi.getScanDetail();
204            if (scanDetail == null) continue;
205            ScanResult result = scanDetail.getScanResult();
206            if (result == null) continue;
207
208            if (scanDetail.getSeen() == 0)
209                continue;
210
211            if ((now_ms - result.seen) > age) continue;
212
213            if (result.is5GHz()) {
214                if (pmiBest5 == null || pmiBest5.compareTo(pmi) < 0) {
215                    pmiBest5 = pmi;
216                }
217            } else if (result.is24GHz()) {
218                if (pmiBest24 == null || pmiBest24.compareTo(pmi) < 0) {
219                    pmiBest24 = pmi;
220                }
221            }
222        }
223
224        WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
225        String logMsg = "Visiblity by passpoint match returned ";
226        if (pmiBest5 != null) {
227            ScanResult result = pmiBest5.getScanDetail().getScanResult();
228            status.rssi5 = result.level;
229            status.age5 = result.seen;
230            status.BSSID5 = result.BSSID;
231            logMsg += "5 GHz BSSID of " + result.BSSID;
232        }
233        if (pmiBest24 != null) {
234            ScanResult result = pmiBest24.getScanDetail().getScanResult();
235            status.rssi24 = result.level;
236            status.age24 = result.seen;
237            status.BSSID24 = result.BSSID;
238            logMsg += "2.4 GHz BSSID of " + result.BSSID;
239        }
240
241        Log.d(TAG, logMsg);
242
243        return status;
244    }
245
246    public WifiConfiguration.Visibility getVisibility(long age) {
247        if (mConfig.isPasspoint()) {
248            return getVisibilityByPasspointMatch(age);
249        } else {
250            return getVisibilityByRssi(age);
251        }
252    }
253
254
255
256    @Override
257    public String toString() {
258        StringBuilder sbuf = new StringBuilder();
259        sbuf.append("Scan Cache:  ").append('\n');
260
261        ArrayList<ScanDetail> list = sort();
262        long now_ms = System.currentTimeMillis();
263        if (list.size() > 0) {
264            for (ScanDetail scanDetail : list) {
265                ScanResult result = scanDetail.getScanResult();
266                long milli = now_ms - scanDetail.getSeen();
267                long ageSec = 0;
268                long ageMin = 0;
269                long ageHour = 0;
270                long ageMilli = 0;
271                long ageDay = 0;
272                if (now_ms > scanDetail.getSeen() && scanDetail.getSeen() > 0) {
273                    ageMilli = milli % 1000;
274                    ageSec   = (milli / 1000) % 60;
275                    ageMin   = (milli / (60*1000)) % 60;
276                    ageHour  = (milli / (60*60*1000)) % 24;
277                    ageDay   = (milli / (24*60*60*1000));
278                }
279                sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
280                sbuf.append(",").append(String.format("%3d", result.level));
281                if (result.autoJoinStatus > 0) {
282                    sbuf.append(",st=").append(result.autoJoinStatus);
283                }
284                if (ageSec > 0 || ageMilli > 0) {
285                    sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
286                            ageHour, ageMin, ageSec, ageMilli));
287                }
288                if (result.numIpConfigFailures > 0) {
289                    sbuf.append(",ipfail=");
290                    sbuf.append(result.numIpConfigFailures);
291                }
292                sbuf.append("} ");
293            }
294            sbuf.append('\n');
295        }
296
297        return sbuf.toString();
298    }
299
300}
301