blob: 86de199c3ea2f626fa825dd14ce4a7afc8af341b [file] [log] [blame]
/*
* Copyright (C) 2016 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.server.wifi;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkAgent;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.PrintWriter;
/**
* Unit tests for {@link com.android.server.wifi.WifiScoreReport}.
*/
@SmallTest
public class WifiScoreReportTest {
class FakeClock extends Clock {
long mWallClockMillis = 1500000000000L;
int mStepMillis = 1001;
@Override
public long getWallClockMillis() {
mWallClockMillis += mStepMillis;
return mWallClockMillis;
}
}
FakeClock mClock;
WifiConfiguration mWifiConfiguration;
WifiScoreReport mWifiScoreReport;
ScanDetailCache mScanDetailCache;
WifiInfo mWifiInfo;
ScoringParams mScoringParams;
@Mock Context mContext;
@Mock NetworkAgent mNetworkAgent;
@Mock Resources mResources;
@Mock WifiMetrics mWifiMetrics;
@Mock PrintWriter mPrintWriter;
/**
* Sets up resource values for testing
*
* See frameworks/base/core/res/res/values/config.xml
*/
private void setUpResources(Resources resources) {
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
.thenReturn(-82);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz))
.thenReturn(-77);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
.thenReturn(-70);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
.thenReturn(-57);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
.thenReturn(-85);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz))
.thenReturn(-80);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
.thenReturn(-73);
when(resources.getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
.thenReturn(-60);
}
/**
* Sets up for unit test
*/
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
setUpResources(mResources);
mWifiInfo = new WifiInfo();
mWifiInfo.setFrequency(2412);
int maxSize = 10;
int trimSize = 5;
when(mContext.getResources()).thenReturn(mResources);
mClock = new FakeClock();
mScoringParams = new ScoringParams(mContext);
mWifiScoreReport = new WifiScoreReport(mScoringParams, mClock);
}
/**
* Cleans up after test
*/
@After
public void tearDown() throws Exception {
mResources = null;
mWifiScoreReport = null;
mWifiMetrics = null;
}
/**
* Test for score reporting
*
* The score should be sent to both the NetworkAgent and the
* WifiMetrics
*/
@Test
public void calculateAndReportScoreSucceeds() throws Exception {
mWifiInfo.setRssi(-77);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
verify(mNetworkAgent).sendNetworkScore(anyInt());
verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
}
/**
* Test for no score report if rssi is invalid
*
* The score should be sent to neither the NetworkAgent nor the
* WifiMetrics
*/
@Test
public void calculateAndReportScoreDoesNotReportWhenRssiIsNotValid() throws Exception {
mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
verify(mNetworkAgent, never()).sendNetworkScore(anyInt());
verify(mWifiMetrics, never()).incrementWifiScoreCount(anyInt());
}
/**
* Test for operation with null NetworkAgent
*
* Expect to not die, and to calculate the score and report to metrics.
*/
@Test
public void networkAgentMayBeNull() throws Exception {
mWifiInfo.setRssi(-33);
mWifiScoreReport.enableVerboseLogging(true);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, null, mWifiMetrics);
verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
}
/**
* Exercise the rates with low RSSI
*
* The setup has a low (not bad) RSSI, and data movement (txSuccessRate) above
* the threshold.
*
* Expect a score above threshold.
*/
@Test
public void allowLowRssiIfDataIsMoving() throws Exception {
mWifiInfo.setRssi(-80);
mWifiInfo.setLinkSpeed(6); // Mbps
mWifiInfo.txSuccessRate = 5.1; // proportional to pps
mWifiInfo.rxSuccessRate = 5.1;
for (int i = 0; i < 10; i++) {
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
int score = mWifiInfo.score;
assertTrue(score > ConnectedScore.WIFI_TRANSITION_SCORE);
}
/**
* Bad RSSI without data moving should allow handoff
*
* The setup has a bad RSSI, and the txSuccessRate is below threshold; several
* scoring iterations are performed.
*
* Expect the score to drop below the handoff threshold.
*/
@Test
public void giveUpOnBadRssiWhenDataIsNotMoving() throws Exception {
mWifiInfo.setRssi(-100);
mWifiInfo.setLinkSpeed(6); // Mbps
mWifiInfo.setFrequency(5220);
mWifiScoreReport.enableVerboseLogging(true);
mWifiInfo.txSuccessRate = 0.1;
mWifiInfo.rxSuccessRate = 0.1;
for (int i = 0; i < 10; i++) {
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
int score = mWifiInfo.score;
assertTrue(score < ConnectedScore.WIFI_TRANSITION_SCORE);
verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
}
/**
* When the score ramps down to the exit theshold, let go.
*/
@Test
public void giveUpOnBadRssiAggressively() throws Exception {
String oops = "giveUpOnBadRssiAggressively";
mWifiInfo.setFrequency(5220);
for (int rssi = -60; rssi >= -83; rssi -= 1) {
mWifiInfo.setRssi(rssi);
oops += " " + mClock.mWallClockMillis + "," + rssi;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
oops += ":" + mWifiInfo.score;
}
int score = mWifiInfo.score;
verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
assertTrue(oops, score < ConnectedScore.WIFI_TRANSITION_SCORE);
}
/**
* RSSI that falls rapidly but does not cross entry threshold should not cause handoff
*
* Expect the score to not drop below the handoff threshold.
*/
@Test
public void stayOnIfRssiDoesNotGetBelowEntryThreshold() throws Exception {
String oops = "didNotStickLanding";
int minScore = 100;
mWifiInfo.setLinkSpeed(6); // Mbps
mWifiInfo.setFrequency(5220);
mWifiScoreReport.enableVerboseLogging(true);
mWifiInfo.txSuccessRate = 0.1;
mWifiInfo.rxSuccessRate = 0.1;
assertTrue(mScoringParams.update("rssi5=-83:-80:-66:-55"));
for (int r = -30; r >= -100; r -= 1) {
int rssi = Math.max(r, -80);
mWifiInfo.setRssi(rssi);
oops += " " + mClock.mWallClockMillis + "," + rssi;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
oops += ":" + mWifiInfo.score;
if (mWifiInfo.score < minScore) minScore = mWifiInfo.score;
}
assertTrue(oops, minScore > ConnectedScore.WIFI_TRANSITION_SCORE);
}
/**
* Don't breach if the success rates are great
*
* Ramp the RSSI down, but maintain a high packet throughput
*
* Expect score to stay above above threshold.
*/
@Test
public void allowTerribleRssiIfDataIsMovingWell() throws Exception {
mWifiInfo.txSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
assertTrue(mWifiInfo.txSuccessRate > 10);
mWifiInfo.setFrequency(5220);
for (int r = -30; r >= -120; r -= 2) {
mWifiInfo.setRssi(r);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertTrue(mWifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE);
}
// If the throughput dips, we should let go
mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() - 0.1;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
// And even if throughput improves again, once we have decided to let go, disregard
// the good rates.
mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
}
/**
* Never ask for nud check when nud=0
*/
@Test
public void neverAskForNudCheckWhenNudKnobIsZero() throws Exception {
assertTrue(mScoringParams.update("nud=0"));
assertEquals(0, mScoringParams.getNudKnob());
mWifiInfo.setFrequency(5220);
for (int rssi = -30; rssi >= -120; rssi -= 1) {
mWifiInfo.setRssi(rssi);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertFalse(mWifiScoreReport.shouldCheckIpLayer());
}
}
/**
* Eventually ask for nud check when nud=1
*/
@Test
public void eventuallyAskForNudCheckWhenNudKnobIsOne() throws Exception {
String oops = "nud=1";
long lastAskedMillis = 0; // Check that we don't send too soon
int asks = 0; // Keep track of how many time we asked
assertTrue(mScoringParams.update("nud=1"));
assertEquals(1, mScoringParams.getNudKnob());
mWifiInfo.setFrequency(5220);
for (int rssi = -40; rssi >= -120; rssi -= 1) {
mWifiInfo.setRssi(rssi);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
boolean ask = mWifiScoreReport.shouldCheckIpLayer();
if (ask) {
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
assertTrue(oops, mClock.mWallClockMillis >= lastAskedMillis + 5000);
lastAskedMillis = mClock.mWallClockMillis;
oops += " " + lastAskedMillis + ":" + mWifiInfo.score;
mWifiScoreReport.noteIpCheck();
asks++;
}
}
assertTrue(oops + " asks:" + asks, asks > 5 && asks < 12);
}
/**
* Ask for more nud checks when nud=10
*/
@Test
public void askForMoreNudChecksWhenNudKnobIsBigger() throws Exception {
String oops = "nud=10";
long lastAskedMillis = 0; // Check that we don't send too soon
int asks = 0; // Keep track of how many time we asked
assertTrue(mScoringParams.update("nud=10"));
assertEquals(10, mScoringParams.getNudKnob());
mWifiInfo.setFrequency(5220);
for (int rssi = -40; rssi >= -120; rssi -= 1) {
mWifiInfo.setRssi(rssi);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
boolean ask = mWifiScoreReport.shouldCheckIpLayer();
if (ask) {
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
assertTrue(oops, mClock.mWallClockMillis >= lastAskedMillis + 5000);
lastAskedMillis = mClock.mWallClockMillis;
oops += " " + lastAskedMillis + ":" + mWifiInfo.score;
mWifiScoreReport.noteIpCheck();
asks++;
}
}
assertTrue(oops + " asks:" + asks, asks > 12 && asks < 80);
}
/**
* Test initial conditions, and after reset()
*/
@Test
public void exerciseReset() throws Exception {
assertFalse(mWifiScoreReport.shouldCheckIpLayer());
mWifiScoreReport.reset();
assertFalse(mWifiScoreReport.shouldCheckIpLayer());
}
/**
* This setup causes some reports to be generated when println
* methods are called, to check for "concurrent" modification
* errors.
*/
private void setupToGenerateAReportWhenPrintlnIsCalled() {
int[] counter = new int[1];
doAnswer(answerVoid((String line) -> {
if (counter[0]++ < 3) {
mWifiScoreReport.calculateAndReportScore(
mWifiInfo, mNetworkAgent, mWifiMetrics);
}
})).when(mPrintWriter).println(anyString());
}
/**
* Test data logging
*/
@Test
public void testDataLogging() throws Exception {
for (int i = 0; i < 10; i++) {
mWifiInfo.setRssi(-65 + i);
mWifiInfo.setLinkSpeed(300);
mWifiInfo.setFrequency(5220);
mWifiInfo.txSuccessRate = 0.1 + i;
mWifiInfo.txRetriesRate = 0.2 + i;
mWifiInfo.txBadRate = 0.01 * i;
mWifiInfo.rxSuccessRate = 0.3 + i;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
setupToGenerateAReportWhenPrintlnIsCalled();
mWifiScoreReport.dump(null, mPrintWriter, null);
verify(mPrintWriter, times(11)).println(anyString());
}
/**
* Test data logging limit
* <p>
* Check that only a bounded amount of data is collected for dumpsys report
*/
@Test
public void testDataLoggingLimit() throws Exception {
for (int i = 0; i < 3620; i++) {
mWifiInfo.setRssi(-65 + i % 20);
mWifiInfo.setLinkSpeed(300);
mWifiInfo.setFrequency(5220);
mWifiInfo.txSuccessRate = 0.1 + i % 100;
mWifiInfo.txRetriesRate = 0.2 + i % 100;
mWifiInfo.txBadRate = 0.0001 * i;
mWifiInfo.rxSuccessRate = 0.3 + i % 200;
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
mWifiScoreReport.dump(null, mPrintWriter, null);
verify(mPrintWriter, atMost(3601)).println(anyString());
}
/**
* Test for staying at below transition score for a certain period of time.
*/
@Test
public void stayAtBelowTransitionScoreForCertainPeriodOfTime() throws Exception {
mWifiScoreReport.enableVerboseLogging(true);
mWifiInfo.setFrequency(5220);
// Reduce RSSI value to fall below the transition score
for (int rssi = -60; rssi >= -83; rssi -= 1) {
mWifiInfo.setRssi(rssi);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
// Then, set high RSSI value to exceed the transition score
mWifiInfo.setRssi(-50);
// 8 seconds elapse
for (int i = 0; i < 8; i++) {
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
// 9 seconds elapse
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertTrue(mWifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE);
}
/**
* Test for resetting the internal timer which is used to keep staying at
* below transition score for a certain period of time.
*/
@Test
public void stayAtBelowTransitionScoreWithReset() throws Exception {
mWifiScoreReport.enableVerboseLogging(true);
mWifiInfo.setFrequency(5220);
// Reduce RSSI value to fall below the transition score
for (int rssi = -60; rssi >= -83; rssi -= 1) {
mWifiInfo.setRssi(rssi);
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
}
assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
// Then, set high RSSI value to exceed the transition score
mWifiInfo.setRssi(-50);
// Reset the internal timer so that no need to wait for 9 seconds
mWifiScoreReport.reset();
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
assertTrue(mWifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE);
}
}