package com.android.server.wifi; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.util.Log; import android.os.SystemClock; import com.android.server.wifi.ScanDetail; import com.android.server.wifi.hotspot2.PasspointMatch; import com.android.server.wifi.hotspot2.PasspointMatchInfo; import com.android.server.wifi.hotspot2.pps.HomeSP; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; class ScanDetailCache { private static final String TAG = "ScanDetailCache"; private WifiConfiguration mConfig; private HashMap<String, ScanDetail> mMap; private HashMap<String, PasspointMatchInfo> mPasspointMatches; private static boolean DBG=false; ScanDetailCache(WifiConfiguration config) { mConfig = config; mMap = new HashMap(); mPasspointMatches = new HashMap(); } void put(ScanDetail scanDetail) { put(scanDetail, null, null); } void put(ScanDetail scanDetail, PasspointMatch match, HomeSP homeSp) { mMap.put(scanDetail.getBSSIDString(), scanDetail); if (match != null && homeSp != null) { mPasspointMatches.put(scanDetail.getBSSIDString(), new PasspointMatchInfo(match, scanDetail, homeSp)); } } ScanResult get(String bssid) { ScanDetail scanDetail = getScanDetail(bssid); return scanDetail == null ? null : scanDetail.getScanResult(); } ScanDetail getScanDetail(String bssid) { return mMap.get(bssid); } void remove(String bssid) { mMap.remove(bssid); } int size() { return mMap.size(); } boolean isEmpty() { return size() == 0; } ScanDetail getFirst() { Iterator<ScanDetail> it = mMap.values().iterator(); return it.hasNext() ? it.next() : null; } Collection<String> keySet() { return mMap.keySet(); } Collection<ScanDetail> values() { return mMap.values(); } public void trim(int num) { int currentSize = mMap.size(); if (currentSize <= num) { return; // Nothing to trim } ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values()); if (list.size() != 0) { // Sort by descending timestamp Collections.sort(list, new Comparator() { public int compare(Object o1, Object o2) { ScanDetail a = (ScanDetail) o1; ScanDetail b = (ScanDetail) o2; if (a.getSeen() > b.getSeen()) { return 1; } if (a.getSeen() < b.getSeen()) { return -1; } return a.getBSSIDString().compareTo(b.getBSSIDString()); } }); } for (int i = 0; i < currentSize - num ; i++) { // Remove oldest results from scan cache ScanDetail result = list.get(i); mMap.remove(result.getBSSIDString()); mPasspointMatches.remove(result.getBSSIDString()); } } /* @hide */ private ArrayList<ScanDetail> sort() { ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values()); if (list.size() != 0) { Collections.sort(list, new Comparator() { public int compare(Object o1, Object o2) { ScanResult a = ((ScanDetail)o1).getScanResult(); ScanResult b = ((ScanDetail)o2).getScanResult(); if (a.numIpConfigFailures > b.numIpConfigFailures) { return 1; } if (a.numIpConfigFailures < b.numIpConfigFailures) { return -1; } if (a.seen > b.seen) { return -1; } if (a.seen < b.seen) { return 1; } if (a.level > b.level) { return -1; } if (a.level < b.level) { return 1; } return a.BSSID.compareTo(b.BSSID); } }); } return list; } public WifiConfiguration.Visibility getVisibilityByRssi(long age) { WifiConfiguration.Visibility status = new WifiConfiguration.Visibility(); long now_ms = System.currentTimeMillis(); long now_elapsed_ms = SystemClock.elapsedRealtime(); for(ScanDetail scanDetail : values()) { ScanResult result = scanDetail.getScanResult(); if (scanDetail.getSeen() == 0) continue; if (result.is5GHz()) { //strictly speaking: [4915, 5825] //number of known BSSID on 5GHz band status.num5 = status.num5 + 1; } else if (result.is24GHz()) { //strictly speaking: [2412, 2482] //number of known BSSID on 2.4Ghz band status.num24 = status.num24 + 1; } if (result.timestamp != 0) { if (DBG) { Log.e("getVisibilityByRssi", " considering " + result.SSID + " " + result.BSSID + " elapsed=" + now_elapsed_ms + " timestamp=" + result.timestamp + " age = " + age); } if ((now_elapsed_ms - (result.timestamp/1000)) > age) continue; } else { // This check the time at which we have received the scan result from supplicant if ((now_ms - result.seen) > age) continue; } if (result.is5GHz()) { if (result.level > status.rssi5) { status.rssi5 = result.level; status.age5 = result.seen; status.BSSID5 = result.BSSID; } } else if (result.is24GHz()) { if (result.level > status.rssi24) { status.rssi24 = result.level; status.age24 = result.seen; status.BSSID24 = result.BSSID; } } } return status; } public WifiConfiguration.Visibility getVisibilityByPasspointMatch(long age) { long now_ms = System.currentTimeMillis(); PasspointMatchInfo pmiBest24 = null, pmiBest5 = null; for(PasspointMatchInfo pmi : mPasspointMatches.values()) { ScanDetail scanDetail = pmi.getScanDetail(); if (scanDetail == null) continue; ScanResult result = scanDetail.getScanResult(); if (result == null) continue; if (scanDetail.getSeen() == 0) continue; if ((now_ms - result.seen) > age) continue; if (result.is5GHz()) { if (pmiBest5 == null || pmiBest5.compareTo(pmi) < 0) { pmiBest5 = pmi; } } else if (result.is24GHz()) { if (pmiBest24 == null || pmiBest24.compareTo(pmi) < 0) { pmiBest24 = pmi; } } } WifiConfiguration.Visibility status = new WifiConfiguration.Visibility(); String logMsg = "Visiblity by passpoint match returned "; if (pmiBest5 != null) { ScanResult result = pmiBest5.getScanDetail().getScanResult(); status.rssi5 = result.level; status.age5 = result.seen; status.BSSID5 = result.BSSID; logMsg += "5 GHz BSSID of " + result.BSSID; } if (pmiBest24 != null) { ScanResult result = pmiBest24.getScanDetail().getScanResult(); status.rssi24 = result.level; status.age24 = result.seen; status.BSSID24 = result.BSSID; logMsg += "2.4 GHz BSSID of " + result.BSSID; } Log.d(TAG, logMsg); return status; } public WifiConfiguration.Visibility getVisibility(long age) { if (mConfig.isPasspoint()) { return getVisibilityByPasspointMatch(age); } else { return getVisibilityByRssi(age); } } @Override public String toString() { StringBuilder sbuf = new StringBuilder(); sbuf.append("Scan Cache: ").append('\n'); ArrayList<ScanDetail> list = sort(); long now_ms = System.currentTimeMillis(); if (list.size() > 0) { for (ScanDetail scanDetail : list) { ScanResult result = scanDetail.getScanResult(); long milli = now_ms - scanDetail.getSeen(); long ageSec = 0; long ageMin = 0; long ageHour = 0; long ageMilli = 0; long ageDay = 0; if (now_ms > scanDetail.getSeen() && scanDetail.getSeen() > 0) { ageMilli = milli % 1000; ageSec = (milli / 1000) % 60; ageMin = (milli / (60*1000)) % 60; ageHour = (milli / (60*60*1000)) % 24; ageDay = (milli / (24*60*60*1000)); } sbuf.append("{").append(result.BSSID).append(",").append(result.frequency); sbuf.append(",").append(String.format("%3d", result.level)); if (result.autoJoinStatus > 0) { sbuf.append(",st=").append(result.autoJoinStatus); } if (ageSec > 0 || ageMilli > 0) { sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay, ageHour, ageMin, ageSec, ageMilli)); } if (result.numIpConfigFailures > 0) { sbuf.append(",ipfail="); sbuf.append(result.numIpConfigFailures); } sbuf.append("} "); } sbuf.append('\n'); } return sbuf.toString(); } }