Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6390fa83 authored by Sundeep Ghuman's avatar Sundeep Ghuman
Browse files

Generate fallback speed label in AccessPoint.java

Cache all seen matching ScoredNetworks, and use these networks to
generate a fallback curve. This cache is kept in sync with WifiTracker's
WifiNetworkScoreCache, hence it does not permanently create any new
objects other than the map itself.

Bug: 63073866
Test: runtest --path
frameworks/base/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java

Change-Id: I0cdf17f63f0238e0e3a8324190f75d82beaa6672
Merged-In: I0cdf17f63f0238e0e3a8324190f75d82beaa6672
parent c164123c
Loading
Loading
Loading
Loading
+111 −31
Original line number Diff line number Diff line
@@ -124,8 +124,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 +144,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 +195,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 +245,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 +322,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 +361,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 +439,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
     */
    boolean update(WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled) {
        boolean scoreChanged = false;
        mScanResultScores.clear();
        if (scoringUiEnabled) {
            scoreChanged = updateScores(scoreCache);
        }
@@ -435,38 +448,79 @@ 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;
            }
            mScoredNetworkCache.put(result.BSSID, score);
        }

            int speed = score.calculateBadge(result.level);
            mScanResultScores.put(result.BSSID, speed);
            mSpeed = Math.max(mSpeed, speed);
        return updateSpeed();
    }

        // set mSpeed to the connected ScanResult if the AccessPoint is the active network
    /**
     * 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 = new NetworkKey(new WifiKey(
                    AccessPoint.convertToQuotedString(ssid), mInfo.getBSSID()));
            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;
    }

    /** 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;
        }

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, String.format("Generating fallbackspeed for %s using cache: %s",
                    getSsidStr(), mScoredNetworkCache));
        }

        return oldSpeed != mSpeed;
        int count = 0;
        int totalSpeed = 0;
        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);
    }

    /**
@@ -582,8 +636,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) {
@@ -942,17 +994,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);
    }

    /**
@@ -1067,6 +1125,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);
        }
@@ -1105,9 +1165,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
            updateRssi();
            int newLevel = getLevel();

            if (newLevel > 0 && newLevel != oldLevel && mAccessPointListener != null) {
            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) {
                pskType = getPskType(result);
@@ -1191,7 +1255,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);
+131 −16
Original line number Diff line number Diff line
@@ -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);
    }
}