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