Merge "Generate fallback speed label in AccessPoint.java"

This commit is contained in:
TreeHugger Robot
2017-08-14 22:05:26 +00:00
committed by Android (Google) Code Review
2 changed files with 244 additions and 46 deletions

View File

@@ -63,6 +63,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -124,8 +125,14 @@ public class AccessPoint implements Comparable<AccessPoint> {
private final ConcurrentHashMap<String, ScanResult> mScanResultCache =
new ConcurrentHashMap<String, ScanResult>(32);
/** Map of BSSIDs to speed values for individual ScanResults. */
private final Map<String, Integer> mScanResultScores = new HashMap<>();
/**
* Map of BSSIDs to scored networks for individual bssids.
*
* <p>This cache should not be evicted with scan results, as the values here are used to
* generate a fallback in the absence of scores for the visible APs.
*/
// TODO(b/63073866): change this to have score eviction logic
private final Map<String, ScoredNetwork> mScoredNetworkCache = new HashMap<>();
/** Maximum age of scan results to hold onto while actively scanning. **/
private static final long MAX_SCAN_RESULT_AGE_MS = 15000;
@@ -138,6 +145,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
static final String KEY_SPEED = "key_speed";
static final String KEY_PSKTYPE = "key_psktype";
static final String KEY_SCANRESULTCACHE = "key_scanresultcache";
static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache";
static final String KEY_CONFIG = "key_config";
static final String KEY_FQDN = "key_fqdn";
static final String KEY_PROVIDER_FRIENDLY_NAME = "key_provider_friendly_name";
@@ -188,7 +196,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
private Object mTag;
private int mSpeed = Speed.NONE;
@Speed private int mSpeed = Speed.NONE;
private boolean mIsScoredNetworkMetered = false;
// used to co-relate internal vs returned accesspoint.
@@ -238,6 +246,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
mScanResultCache.put(result.BSSID, result);
}
}
if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) {
ArrayList<ScoredNetwork> scoredNetworkArrayList =
savedState.getParcelableArrayList(KEY_SCOREDNETWORKCACHE);
for (ScoredNetwork score : scoredNetworkArrayList) {
mScoredNetworkCache.put(score.networkKey.wifiKey.bssid, score);
}
}
if (savedState.containsKey(KEY_FQDN)) {
mFqdn = savedState.getString(KEY_FQDN);
}
@@ -308,8 +323,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
this.mNetworkInfo = that.mNetworkInfo;
this.mScanResultCache.clear();
this.mScanResultCache.putAll(that.mScanResultCache);
this.mScanResultScores.clear();
this.mScanResultScores.putAll(that.mScanResultScores);
this.mScoredNetworkCache.clear();
this.mScoredNetworkCache.putAll(that.mScoredNetworkCache);
this.mId = that.mId;
this.mSpeed = that.mSpeed;
this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
@@ -347,7 +362,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
if (isSaved() && !other.isSaved()) return -1;
if (!isSaved() && other.isSaved()) return 1;
// Faster speeds go before slower speeds
// Faster speeds go before slower speeds - but only if visible change in speed label
if (getSpeed() != other.getSpeed()) {
return other.getSpeed() - getSpeed();
}
@@ -425,7 +440,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
*/
boolean update(WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled) {
boolean scoreChanged = false;
mScanResultScores.clear();
if (scoringUiEnabled) {
scoreChanged = updateScores(scoreCache);
}
@@ -435,37 +449,80 @@ public class AccessPoint implements Comparable<AccessPoint> {
/**
* Updates the AccessPoint rankingScore and speed, returning true if the data has changed.
*
* <p>Precondition: {@link #mRssi} is up to date before invoking this method.
*
* @param scoreCache The score cache to use to retrieve scores.
* @return true if the set speed has changed
*/
private boolean updateScores(WifiNetworkScoreCache scoreCache) {
int oldSpeed = mSpeed;
mSpeed = Speed.NONE;
for (ScanResult result : mScanResultCache.values()) {
ScoredNetwork score = scoreCache.getScoredNetwork(result);
if (score == null) {
continue;
}
int speed = score.calculateBadge(result.level);
mScanResultScores.put(result.BSSID, speed);
mSpeed = Math.max(mSpeed, speed);
mScoredNetworkCache.put(result.BSSID, score);
}
// set mSpeed to the connected ScanResult if the AccessPoint is the active network
return updateSpeed();
}
/**
* Updates the internal speed, returning true if the update resulted in a speed label change.
*/
private boolean updateSpeed() {
int oldSpeed = mSpeed;
mSpeed = generateAverageSpeedForSsid();
// set speed to the connected ScanResult if the AccessPoint is the active network
if (isActive() && mInfo != null) {
NetworkKey key = NetworkKey.createFromWifiInfo(mInfo);
ScoredNetwork score = scoreCache.getScoredNetwork(key);
ScoredNetwork score = mScoredNetworkCache.get(mInfo.getBSSID());
if (score != null) {
mSpeed = score.calculateBadge(mInfo.getRssi());
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Set score using specific access point curve for connected AP: "
+ getSsidStr());
}
// TODO(b/63073866): Map using getLevel rather than specific rssi value so score
// doesn't change without a visible wifi bar change.
int speed = score.calculateBadge(mInfo.getRssi());
if (speed != Speed.NONE) {
mSpeed = speed;
}
}
}
if(WifiTracker.sVerboseLogging) {
boolean changed = oldSpeed != mSpeed;
if(WifiTracker.sVerboseLogging && changed) {
Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed));
}
return changed;
}
return oldSpeed != mSpeed;
/** Creates a speed value for the current {@link #mRssi} by averaging all non zero badges. */
@Speed private int generateAverageSpeedForSsid() {
if (mScoredNetworkCache.isEmpty()) {
return Speed.NONE;
}
int count = 0;
int totalSpeed = 0;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, String.format("Generating fallbackspeed for %s using cache: %s",
getSsidStr(), mScoredNetworkCache));
}
for (ScoredNetwork score : mScoredNetworkCache.values()) {
int speed = score.calculateBadge(mRssi);
if (speed != Speed.NONE) {
count++;
totalSpeed += speed;
}
}
int speed = count == 0 ? Speed.NONE : totalSpeed / count;
if (WifiTracker.sVerboseLogging) {
Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed));
}
return roundToClosestSpeedEnum(speed);
}
/**
@@ -580,8 +637,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
/** Updates {@link #mSeen} based on the scan result cache. */
private void updateSeen() {
// TODO(sghuman): Set to now if connected
long seen = 0;
for (ScanResult result : mScanResultCache.values()) {
if (result.timestamp > seen) {
@@ -940,17 +995,23 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
stringBuilder.append("=").append(result.frequency);
stringBuilder.append(",").append(result.level);
if (hasSpeed(result)) {
int speed = getSpecificApSpeed(result);
if (speed != Speed.NONE) {
stringBuilder.append(",")
.append(getSpeedLabel(mScanResultScores.get(result.BSSID)));
.append(getSpeedLabel(speed));
}
stringBuilder.append("}");
return stringBuilder.toString();
}
private boolean hasSpeed(ScanResult result) {
return mScanResultScores.containsKey(result.BSSID)
&& mScanResultScores.get(result.BSSID) != Speed.NONE;
@Speed private int getSpecificApSpeed(ScanResult result) {
ScoredNetwork score = mScoredNetworkCache.get(result.BSSID);
if (score == null) {
return Speed.NONE;
}
// For debugging purposes we may want to use mRssi rather than result.level as the average
// speed wil be determined by mRssi
return score.calculateBadge(result.level);
}
/**
@@ -1065,6 +1126,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
evictOldScanResults();
savedState.putParcelableArrayList(KEY_SCANRESULTCACHE,
new ArrayList<ScanResult>(mScanResultCache.values()));
savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE,
new ArrayList<>(mScoredNetworkCache.values()));
if (mNetworkInfo != null) {
savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
}
@@ -1103,8 +1166,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
updateRssi();
int newLevel = getLevel();
if (newLevel > 0 && newLevel != oldLevel && mAccessPointListener != null) {
mAccessPointListener.onLevelChanged(this);
if (newLevel > 0 && newLevel != oldLevel) {
// Only update labels on visible rssi changes
updateSpeed();
if (mAccessPointListener != null) {
mAccessPointListener.onLevelChanged(this);
}
}
// This flag only comes from scans, is not easily saved in config
if (security == SECURITY_PSK) {
@@ -1189,7 +1256,23 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
@Nullable
private String getSpeedLabel(int speed) {
@Speed
private int roundToClosestSpeedEnum(int speed) {
if (speed < Speed.SLOW) {
return Speed.NONE;
} else if (speed < (Speed.SLOW + Speed.MODERATE) / 2) {
return Speed.SLOW;
} else if (speed < (Speed.MODERATE + Speed.FAST) / 2) {
return Speed.MODERATE;
} else if (speed < (Speed.FAST + Speed.VERY_FAST) / 2) {
return Speed.FAST;
} else {
return Speed.VERY_FAST;
}
}
@Nullable
private String getSpeedLabel(@Speed int speed) {
switch (speed) {
case Speed.VERY_FAST:
return mContext.getString(R.string.speed_label_very_fast);

View File

@@ -66,10 +66,24 @@ import java.util.Collections;
public class AccessPointTest {
private static final String TEST_SSID = "test_ssid";
private static final int NUM_SCAN_RESULTS = 5;
private static final ArrayList<ScanResult> SCAN_RESULTS = buildScanResultCache();
private Context mContext;
@Mock private RssiCurve mockBadgeCurve;
@Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache;
private static ScanResult createScanResult(String ssid, String bssid, int rssi) {
ScanResult scanResult = new ScanResult();
scanResult.SSID = ssid;
scanResult.level = rssi;
scanResult.BSSID = bssid;
scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
scanResult.capabilities = "";
return scanResult;
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -400,7 +414,7 @@ public class AccessPointTest {
}
@Test
public void testSpeedLabel_isDerivedFromConnectedBssid() {
public void testSpeedLabel_isDerivedFromConnectedBssidWhenScoreAvailable() {
int rssi = -55;
String bssid = "00:00:00:00:00:00";
int networkId = 123;
@@ -411,24 +425,42 @@ public class AccessPointTest {
info.setBSSID(bssid);
info.setNetworkId(networkId);
ArrayList<ScanResult> scanResults = new ArrayList<>();
ScanResult scanResultUnconnected = createScanResult(TEST_SSID, "11:11:11:11:11:11", rssi);
scanResults.add(scanResultUnconnected);
ScanResult scanResultConnected = createScanResult(TEST_SSID, bssid, rssi);
scanResults.add(scanResultConnected);
AccessPoint ap =
new TestAccessPointBuilder(mContext)
.setActive(true)
.setNetworkId(networkId)
.setSsid(TEST_SSID)
.setScanResultCache(buildScanResultCache())
.setScanResultCache(scanResults)
.setWifiInfo(info)
.build();
NetworkKey key = new NetworkKey(new WifiKey('"' + TEST_SSID + '"', bssid));
when(mockWifiNetworkScoreCache.getScoredNetwork(key))
when(mockWifiNetworkScoreCache.getScoredNetwork(scanResultUnconnected))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.FAST);
when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) Speed.SLOW);
int connectedSpeed = Speed.VERY_FAST;
RssiCurve connectedBadgeCurve = mock(RssiCurve.class);
Bundle attr1 = new Bundle();
attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, connectedBadgeCurve);
ScoredNetwork connectedScore = new ScoredNetwork(
NetworkKey.createFromScanResult(scanResultConnected),
connectedBadgeCurve,
false /* meteredHint */,
attr1);
when(mockWifiNetworkScoreCache.getScoredNetwork(scanResultConnected))
.thenReturn(connectedScore);
when(connectedBadgeCurve.lookupScore(anyInt())).thenReturn((byte) connectedSpeed);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
verify(mockWifiNetworkScoreCache, times(2)).getScoredNetwork(key);
assertThat(ap.getSpeed()).isEqualTo(AccessPoint.Speed.FAST);
assertThat(ap.getSpeed()).isEqualTo(connectedSpeed);
}
@Test
@@ -562,8 +594,13 @@ public class AccessPointTest {
}
private ScoredNetwork buildScoredNetworkWithMockBadgeCurve() {
return buildScoredNetworkWithGivenBadgeCurve(mockBadgeCurve);
}
private ScoredNetwork buildScoredNetworkWithGivenBadgeCurve(RssiCurve badgeCurve) {
Bundle attr1 = new Bundle();
attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve);
attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgeCurve);
return new ScoredNetwork(
new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
mockBadgeCurve,
@@ -574,19 +611,14 @@ public class AccessPointTest {
private AccessPoint createAccessPointWithScanResultCache() {
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = buildScanResultCache();
bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, scanResults);
bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, SCAN_RESULTS);
return new AccessPoint(mContext, bundle);
}
private ArrayList<ScanResult> buildScanResultCache() {
private static ArrayList<ScanResult> buildScanResultCache() {
ArrayList<ScanResult> scanResults = new ArrayList<>();
for (int i = 0; i < 5; i++) {
ScanResult scanResult = new ScanResult();
scanResult.level = i;
scanResult.BSSID = "bssid-" + i;
scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
scanResult.capabilities = "";
ScanResult scanResult = createScanResult(TEST_SSID, "bssid-" + i, i);
scanResults.add(scanResult);
}
return scanResults;
@@ -849,4 +881,87 @@ public class AccessPointTest {
ap.update(null, wifiInfo, networkInfo);
}
@Test
public void testSpeedLabelAveragesAllBssidScores() {
AccessPoint ap = createAccessPointWithScanResultCache();
int speed1 = Speed.MODERATE;
RssiCurve badgeCurve1 = mock(RssiCurve.class);
when(badgeCurve1.lookupScore(anyInt())).thenReturn((byte) speed1);
when(mockWifiNetworkScoreCache.getScoredNetwork(SCAN_RESULTS.get(0)))
.thenReturn(buildScoredNetworkWithGivenBadgeCurve(badgeCurve1));
int speed2 = Speed.VERY_FAST;
RssiCurve badgeCurve2 = mock(RssiCurve.class);
when(badgeCurve2.lookupScore(anyInt())).thenReturn((byte) speed2);
when(mockWifiNetworkScoreCache.getScoredNetwork(SCAN_RESULTS.get(1)))
.thenReturn(buildScoredNetworkWithGivenBadgeCurve(badgeCurve2));
int expectedSpeed = (speed1 + speed2) / 2;
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
assertThat(ap.getSpeed()).isEqualTo(expectedSpeed);
}
@Test
public void testSpeedLabelAverageIgnoresNoSpeedScores() {
AccessPoint ap = createAccessPointWithScanResultCache();
int speed1 = Speed.VERY_FAST;
RssiCurve badgeCurve1 = mock(RssiCurve.class);
when(badgeCurve1.lookupScore(anyInt())).thenReturn((byte) speed1);
when(mockWifiNetworkScoreCache.getScoredNetwork(SCAN_RESULTS.get(0)))
.thenReturn(buildScoredNetworkWithGivenBadgeCurve(badgeCurve1));
int speed2 = Speed.NONE;
RssiCurve badgeCurve2 = mock(RssiCurve.class);
when(badgeCurve2.lookupScore(anyInt())).thenReturn((byte) speed2);
when(mockWifiNetworkScoreCache.getScoredNetwork(SCAN_RESULTS.get(1)))
.thenReturn(buildScoredNetworkWithGivenBadgeCurve(badgeCurve2));
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
assertThat(ap.getSpeed()).isEqualTo(speed1);
}
@Test
public void testSpeedLabelUsesFallbackScoreWhenConnectedAccessPointScoreUnavailable() {
int rssi = -55;
String bssid = "00:00:00:00:00:00";
int networkId = 123;
WifiInfo info = new WifiInfo();
info.setRssi(rssi);
info.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
info.setBSSID(bssid);
info.setNetworkId(networkId);
ArrayList<ScanResult> scanResults = new ArrayList<>();
ScanResult scanResultUnconnected = createScanResult(TEST_SSID, "11:11:11:11:11:11", rssi);
scanResults.add(scanResultUnconnected);
ScanResult scanResultConnected = createScanResult(TEST_SSID, bssid, rssi);
scanResults.add(scanResultConnected);
AccessPoint ap =
new TestAccessPointBuilder(mContext)
.setActive(true)
.setNetworkId(networkId)
.setSsid(TEST_SSID)
.setScanResultCache(scanResults)
.setWifiInfo(info)
.build();
int fallbackSpeed = Speed.SLOW;
when(mockWifiNetworkScoreCache.getScoredNetwork(scanResultUnconnected))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) fallbackSpeed);
when(mockWifiNetworkScoreCache.getScoredNetwork(scanResultConnected))
.thenReturn(null);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
assertThat(ap.getSpeed()).isEqualTo(fallbackSpeed);
}
}