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

Commit 995223c9 authored by Sundeep Ghuman's avatar Sundeep Ghuman Committed by android-build-merger
Browse files

Merge "Implement score cache eviction in AccessPoint." into oc-mr1-dev

am: f563cc5a

Change-Id: I2fb9fd06d77023c814d3dedc4ca4cc653ae4cd01
parents 62a96b2e f563cc5a
Loading
Loading
Loading
Loading
+40 −16
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.support.annotation.NonNull;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.Log;

@@ -130,11 +131,14 @@ public class AccessPoint implements Comparable<AccessPoint> {
     * <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<>();
    private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>();

    /** Maximum age in millis of cached scored networks in {@link #mScoredNetworkCache}. */
    @VisibleForTesting static final long MAX_CACHED_SCORE_AGE_MILLIS =
            24 * DateUtils.DAY_IN_MILLIS;

    /** Maximum age of scan results to hold onto while actively scanning. **/
    private static final long MAX_SCAN_RESULT_AGE_MS = 15000;
    private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;

    static final String KEY_NETWORKINFO = "key_networkinfo";
    static final String KEY_WIFIINFO = "key_wifiinfo";
@@ -246,10 +250,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
            }
        }
        if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) {
            ArrayList<ScoredNetwork> scoredNetworkArrayList =
            ArrayList<TimestampedScoredNetwork> scoredNetworkArrayList =
                    savedState.getParcelableArrayList(KEY_SCOREDNETWORKCACHE);
            for (ScoredNetwork score : scoredNetworkArrayList) {
                mScoredNetworkCache.put(score.networkKey.wifiKey.bssid, score);
            for (TimestampedScoredNetwork timedScore : scoredNetworkArrayList) {
                mScoredNetworkCache.put(timedScore.getScore().networkKey.wifiKey.bssid, timedScore);
            }
        }
        if (savedState.containsKey(KEY_FQDN)) {
@@ -448,20 +452,40 @@ public class AccessPoint implements Comparable<AccessPoint> {
    /**
     * Updates the AccessPoint rankingScore and speed, returning true if the data has changed.
     *
     * <p>Any cached {@link TimestampedScoredNetwork} objects older than
     * {@link #MAX_CACHED_SCORE_AGE_MILLIS} will be removed when this method is invoked.
     *
     * <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) {
        long nowMillis = SystemClock.elapsedRealtime();
        for (ScanResult result : mScanResultCache.values()) {
            ScoredNetwork score = scoreCache.getScoredNetwork(result);
            if (score == null) {
                continue;
            }
            mScoredNetworkCache.put(result.BSSID, score);
            TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(result.BSSID);
            if (timedScore == null) {
                mScoredNetworkCache.put(
                        result.BSSID, new TimestampedScoredNetwork(score, nowMillis));
            } else {
                // Update data since the has been seen in the score cache
                timedScore.update(score, nowMillis);
            }
        }

        // Remove old cached networks
        long evictionCutoff = nowMillis - MAX_CACHED_SCORE_AGE_MILLIS;
        Iterator<TimestampedScoredNetwork> iterator = mScoredNetworkCache.values().iterator();
        iterator.forEachRemaining(timestampedScoredNetwork -> {
            if (timestampedScoredNetwork.getUpdatedTimestampMillis() < evictionCutoff) {
                iterator.remove();
            }
        });

        return updateSpeed();
    }

@@ -474,15 +498,15 @@ public class AccessPoint implements Comparable<AccessPoint> {

        // set speed to the connected ScanResult if the AccessPoint is the active network
        if (isActive() && mInfo != null) {
            ScoredNetwork score = mScoredNetworkCache.get(mInfo.getBSSID());
            if (score != null) {
            TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(mInfo.getBSSID());
            if (timedScore != null) {
                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());
                int speed = timedScore.getScore().calculateBadge(mInfo.getRssi());
                if (speed != Speed.NONE) {
                    mSpeed = speed;
                }
@@ -509,8 +533,8 @@ public class AccessPoint implements Comparable<AccessPoint> {

        int count = 0;
        int totalSpeed = 0;
        for (ScoredNetwork score : mScoredNetworkCache.values()) {
            int speed = score.calculateBadge(mRssi);
        for (TimestampedScoredNetwork timedScore : mScoredNetworkCache.values()) {
            int speed = timedScore.getScore().calculateBadge(mRssi);
            if (speed != Speed.NONE) {
                count++;
                totalSpeed += speed;
@@ -555,7 +579,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
        for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
            ScanResult result = iter.next();
            // result timestamp is in microseconds
            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MS) {
            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
                iter.remove();
            }
        }
@@ -1004,13 +1028,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
    }

    @Speed private int getSpecificApSpeed(ScanResult result) {
        ScoredNetwork score = mScoredNetworkCache.get(result.BSSID);
        if (score == null) {
        TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(result.BSSID);
        if (timedScore == 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);
        return timedScore.getScore().calculateBadge(result.level);
    }

    /**
+11 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.Bundle;
import android.support.annotation.Keep;

import com.android.settingslib.wifi.AccessPoint.Speed;

import java.util.ArrayList;
@@ -58,6 +59,7 @@ public class TestAccessPointBuilder {

    Context mContext;
    private ArrayList<ScanResult> mScanResultCache;
    private ArrayList<TimestampedScoredNetwork> mScoredNetworkCache;

    @Keep
    public TestAccessPointBuilder(Context context) {
@@ -85,6 +87,9 @@ public class TestAccessPointBuilder {
        if (mScanResultCache != null) {
            bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, mScanResultCache);
        }
        if (mScoredNetworkCache != null) {
            bundle.putParcelableArrayList(AccessPoint.KEY_SCOREDNETWORKCACHE, mScoredNetworkCache);
        }
        bundle.putInt(AccessPoint.KEY_SECURITY, mSecurity);
        bundle.putInt(AccessPoint.KEY_SPEED, mSpeed);
        bundle.putBoolean(AccessPoint.KEY_IS_CARRIER_AP, mIsCarrierAp);
@@ -238,4 +243,10 @@ public class TestAccessPointBuilder {
        mCarrierName = carrierName;
        return this;
    }

    public TestAccessPointBuilder setScoredNetworkCache(
            ArrayList<TimestampedScoredNetwork> scoredNetworkCache) {
        mScoredNetworkCache = scoredNetworkCache;
        return this;
    }
}
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.wifi;

import android.net.ScoredNetwork;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * Data encapsulation object to associate a time with a {@link ScoredNetwork}
 */
class TimestampedScoredNetwork implements Parcelable {
    private ScoredNetwork mScore;
    private long mUpdatedTimestampMillis;

    TimestampedScoredNetwork(ScoredNetwork score, long updatedTimestampMillis) {
        mScore = score;
        mUpdatedTimestampMillis = updatedTimestampMillis;
    }

    protected TimestampedScoredNetwork(Parcel in) {
        mScore = ScoredNetwork.CREATOR.createFromParcel(in);
        mUpdatedTimestampMillis = in.readLong();
    }

    public void update(ScoredNetwork score, long updatedTimestampMillis) {
        mScore = score;
        mUpdatedTimestampMillis = updatedTimestampMillis;
    }

    public ScoredNetwork getScore() {
        return mScore;
    }

    public long getUpdatedTimestampMillis() {
        return mUpdatedTimestampMillis;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(mScore, flags);
        dest.writeLong(mUpdatedTimestampMillis);
    }

    public static final Creator<TimestampedScoredNetwork> CREATOR =
            new Creator<TimestampedScoredNetwork>() {
                @Override
                public TimestampedScoredNetwork createFromParcel(Parcel in) {
                    return new TimestampedScoredNetwork(in);
                }

                @Override
                public TimestampedScoredNetwork[] newArray(int size) {
                    return new TimestampedScoredNetwork[size];
                }
            };
}
+128 −6
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -50,8 +48,8 @@ import android.text.SpannableString;
import android.text.style.TtsSpan;

import com.android.settingslib.R;

import com.android.settingslib.wifi.AccessPoint.Speed;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,17 +57,22 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class AccessPointTest {

    private static final String TEST_SSID = "test_ssid";
    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 static final RssiCurve FAST_BADGE_CURVE =
            new RssiCurve(-150, 10, new byte[]{Speed.FAST});
    public static final String TEST_BSSID = "00:00:00:00:00:00";

    private Context mContext;
    @Mock private RssiCurve mockBadgeCurve;
    @Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache;
@@ -602,8 +605,8 @@ public class AccessPointTest {
        Bundle attr1 = new Bundle();
        attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgeCurve);
        return new ScoredNetwork(
                new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
                mockBadgeCurve,
                new NetworkKey(new WifiKey(TEST_SSID, TEST_BSSID)),
                badgeCurve,
                false /* meteredHint */,
                attr1);

@@ -632,6 +635,18 @@ public class AccessPointTest {
        return configuration;
    }

    private AccessPoint createApWithFastTimestampedScoredNetworkCache(
            long elapsedTimeMillis) {
        TimestampedScoredNetwork recentScore = new TimestampedScoredNetwork(
                buildScoredNetworkWithGivenBadgeCurve(FAST_BADGE_CURVE),
                elapsedTimeMillis);
        return new TestAccessPointBuilder(mContext)
                .setSsid(TEST_SSID)
                .setScoredNetworkCache(
                        new ArrayList<>(Arrays.asList(recentScore)))
                .build();
    }

    /**
    * Assert that the first AccessPoint appears before the second AccessPoint
    * once sorting has been completed.
@@ -964,4 +979,111 @@ public class AccessPointTest {

        assertThat(ap.getSpeed()).isEqualTo(fallbackSpeed);
    }

    @Test
    public void testScoredNetworkCacheBundling() {
        long timeMillis = SystemClock.elapsedRealtime();
        AccessPoint ap = createApWithFastTimestampedScoredNetworkCache(timeMillis);
        Bundle bundle = new Bundle();
        ap.saveWifiState(bundle);

        ArrayList<TimestampedScoredNetwork> list =
                bundle.getParcelableArrayList(AccessPoint.KEY_SCOREDNETWORKCACHE);
        assertThat(list).hasSize(1);
        assertThat(list.get(0).getUpdatedTimestampMillis()).isEqualTo(timeMillis);

        RssiCurve curve = list.get(0).getScore().attributes.getParcelable(
                ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE);
        assertThat(curve).isEqualTo(FAST_BADGE_CURVE);
    }

    @Test
    public void testRecentNetworkScoresAreUsedForSpeedLabelGeneration() {
        AccessPoint ap =
                createApWithFastTimestampedScoredNetworkCache(SystemClock.elapsedRealtime());

        ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);

        assertThat(ap.getSpeed()).isEqualTo(Speed.FAST);
    }

    @Test
    public void testNetworkScoresAreUsedForSpeedLabelGenerationWhenWithinAgeRange() {
        long withinRangeTimeMillis =
                SystemClock.elapsedRealtime() - (AccessPoint.MAX_CACHED_SCORE_AGE_MILLIS - 10000);
        AccessPoint ap =
                createApWithFastTimestampedScoredNetworkCache(withinRangeTimeMillis);

        ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);

        assertThat(ap.getSpeed()).isEqualTo(Speed.FAST);
    }

    @Test
    public void testOldNetworkScoresAreNotUsedForSpeedLabelGeneration() {
        long tooOldTimeMillis =
                SystemClock.elapsedRealtime() - (AccessPoint.MAX_CACHED_SCORE_AGE_MILLIS + 1);
        AccessPoint ap =
                createApWithFastTimestampedScoredNetworkCache(tooOldTimeMillis);

        ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);

        assertThat(ap.getSpeed()).isEqualTo(Speed.NONE);
    }

    @Test
    public void testUpdateScoresRefreshesScoredNetworkCacheTimestamps () {
        long tooOldTimeMillis =
                SystemClock.elapsedRealtime() - (AccessPoint.MAX_CACHED_SCORE_AGE_MILLIS + 1);

        ScoredNetwork scoredNetwork = buildScoredNetworkWithGivenBadgeCurve(FAST_BADGE_CURVE);
        TimestampedScoredNetwork recentScore = new TimestampedScoredNetwork(
                scoredNetwork,
                tooOldTimeMillis);
        AccessPoint ap = new TestAccessPointBuilder(mContext)
                .setSsid(TEST_SSID)
                .setBssid(TEST_BSSID)
                .setActive(true)
                .setScoredNetworkCache(
                        new ArrayList(Arrays.asList(recentScore)))
                .setScanResultCache(SCAN_RESULTS)
                .build();

        when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
                .thenReturn(scoredNetwork);

        ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);

        // Fast should still be returned since cache was updated with recent time
        assertThat(ap.getSpeed()).isEqualTo(Speed.FAST);
    }

    @Test
    public void testUpdateScoresRefreshesScoredNetworkCacheWithNewSpeed () {
        long tooOldTimeMillis =
                SystemClock.elapsedRealtime() - (AccessPoint.MAX_CACHED_SCORE_AGE_MILLIS + 1);

        ScoredNetwork scoredNetwork = buildScoredNetworkWithGivenBadgeCurve(FAST_BADGE_CURVE);
        TimestampedScoredNetwork recentScore = new TimestampedScoredNetwork(
                scoredNetwork,
                tooOldTimeMillis);
        AccessPoint ap = new TestAccessPointBuilder(mContext)
                .setSsid(TEST_SSID)
                .setBssid(TEST_BSSID)
                .setActive(true)
                .setScoredNetworkCache(
                        new ArrayList(Arrays.asList(recentScore)))
                .setScanResultCache(SCAN_RESULTS)
                .build();

        int newSpeed = Speed.MODERATE;
        when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
                .thenReturn(buildScoredNetworkWithMockBadgeCurve());
        when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) newSpeed);

        ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);

        // Fast should still be returned since cache was updated with recent time
        assertThat(ap.getSpeed()).isEqualTo(newSpeed);
    }
}