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

Commit f563cc5a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents b1bd8409 a419b9b4
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);
    }
}