Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertTrue;
     22 import static org.mockito.AdditionalAnswers.answerVoid;
     23 import static org.mockito.ArgumentMatchers.anyString;
     24 import static org.mockito.Mockito.anyInt;
     25 import static org.mockito.Mockito.atLeast;
     26 import static org.mockito.Mockito.atMost;
     27 import static org.mockito.Mockito.doAnswer;
     28 import static org.mockito.Mockito.never;
     29 import static org.mockito.Mockito.times;
     30 import static org.mockito.Mockito.verify;
     31 import static org.mockito.Mockito.when;
     32 
     33 import android.content.Context;
     34 import android.content.res.Resources;
     35 import android.net.NetworkAgent;
     36 import android.net.wifi.WifiConfiguration;
     37 import android.net.wifi.WifiInfo;
     38 
     39 import com.android.internal.R;
     40 
     41 import org.junit.After;
     42 import org.junit.Before;
     43 import org.junit.Test;
     44 import org.mockito.Mock;
     45 import org.mockito.MockitoAnnotations;
     46 
     47 import java.io.PrintWriter;
     48 
     49 /**
     50  * Unit tests for {@link com.android.server.wifi.WifiScoreReport}.
     51  */
     52 public class WifiScoreReportTest {
     53 
     54 
     55     class FakeClock extends Clock {
     56         long mWallClockMillis = 1500000000000L;
     57         int mStepMillis = 1001;
     58 
     59         @Override
     60         public long getWallClockMillis() {
     61             mWallClockMillis += mStepMillis;
     62             return mWallClockMillis;
     63         }
     64     }
     65 
     66     FakeClock mClock;
     67     WifiConfiguration mWifiConfiguration;
     68     WifiScoreReport mWifiScoreReport;
     69     ScanDetailCache mScanDetailCache;
     70     WifiInfo mWifiInfo;
     71     ScoringParams mScoringParams;
     72     @Mock Context mContext;
     73     @Mock NetworkAgent mNetworkAgent;
     74     @Mock Resources mResources;
     75     @Mock WifiMetrics mWifiMetrics;
     76     @Mock PrintWriter mPrintWriter;
     77 
     78     /**
     79      * Sets up resource values for testing
     80      *
     81      * See frameworks/base/core/res/res/values/config.xml
     82      */
     83     private void setUpResources(Resources resources) {
     84         when(resources.getInteger(
     85                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
     86             .thenReturn(-82);
     87         when(resources.getInteger(
     88                 R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz))
     89             .thenReturn(-77);
     90         when(resources.getInteger(
     91                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
     92             .thenReturn(-70);
     93         when(resources.getInteger(
     94                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
     95             .thenReturn(-57);
     96         when(resources.getInteger(
     97                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
     98             .thenReturn(-85);
     99         when(resources.getInteger(
    100                 R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz))
    101             .thenReturn(-80);
    102         when(resources.getInteger(
    103                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
    104             .thenReturn(-73);
    105         when(resources.getInteger(
    106                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
    107             .thenReturn(-60);
    108         when(resources.getInteger(
    109                 R.integer.config_wifi_framework_wifi_score_bad_link_speed_24))
    110             .thenReturn(6); // Mbps
    111         when(resources.getInteger(
    112                 R.integer.config_wifi_framework_wifi_score_bad_link_speed_5))
    113             .thenReturn(12);
    114         when(resources.getInteger(
    115                 R.integer.config_wifi_framework_wifi_score_good_link_speed_24))
    116             .thenReturn(24);
    117         when(resources.getInteger(
    118                 R.integer.config_wifi_framework_wifi_score_good_link_speed_5))
    119             .thenReturn(36);
    120     }
    121 
    122     /**
    123      * Sets up for unit test
    124      */
    125     @Before
    126     public void setUp() throws Exception {
    127         MockitoAnnotations.initMocks(this);
    128         setUpResources(mResources);
    129         mWifiInfo = new WifiInfo();
    130         mWifiInfo.setFrequency(2412);
    131         int maxSize = 10;
    132         int trimSize = 5;
    133         when(mContext.getResources()).thenReturn(mResources);
    134         mClock = new FakeClock();
    135         mScoringParams = new ScoringParams(mContext);
    136         mWifiScoreReport = new WifiScoreReport(mScoringParams, mClock);
    137     }
    138 
    139     /**
    140      * Cleans up after test
    141      */
    142     @After
    143     public void tearDown() throws Exception {
    144         mResources = null;
    145         mWifiScoreReport = null;
    146         mWifiMetrics = null;
    147     }
    148 
    149     /**
    150      * Test for score reporting
    151      *
    152      * The score should be sent to both the NetworkAgent and the
    153      * WifiMetrics
    154      */
    155     @Test
    156     public void calculateAndReportScoreSucceeds() throws Exception {
    157         mWifiInfo.setRssi(-77);
    158         mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    159         verify(mNetworkAgent).sendNetworkScore(anyInt());
    160         verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
    161     }
    162 
    163     /**
    164      * Test for no score report if rssi is invalid
    165      *
    166      * The score should be sent to neither the NetworkAgent nor the
    167      * WifiMetrics
    168      */
    169     @Test
    170     public void calculateAndReportScoreDoesNotReportWhenRssiIsNotValid() throws Exception {
    171         mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
    172         mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    173         verify(mNetworkAgent, never()).sendNetworkScore(anyInt());
    174         verify(mWifiMetrics, never()).incrementWifiScoreCount(anyInt());
    175     }
    176 
    177     /**
    178      * Test for operation with null NetworkAgent
    179      *
    180      * Expect to not die, and to calculate the score and report to metrics.
    181      */
    182     @Test
    183     public void networkAgentMayBeNull() throws Exception {
    184         mWifiInfo.setRssi(-33);
    185         mWifiScoreReport.enableVerboseLogging(true);
    186         mWifiScoreReport.calculateAndReportScore(mWifiInfo, null, mWifiMetrics);
    187         verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
    188     }
    189 
    190     /**
    191      * Exercise the rates with low RSSI
    192      *
    193      * The setup has a low (not bad) RSSI, and data movement (txSuccessRate) above
    194      * the threshold.
    195      *
    196      * Expect a score above threshold.
    197      */
    198     @Test
    199     public void allowLowRssiIfDataIsMoving() throws Exception {
    200         mWifiInfo.setRssi(-80);
    201         mWifiInfo.setLinkSpeed(6); // Mbps
    202         mWifiInfo.txSuccessRate = 5.1; // proportional to pps
    203         mWifiInfo.rxSuccessRate = 5.1;
    204         for (int i = 0; i < 10; i++) {
    205             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    206         }
    207         int score = mWifiInfo.score;
    208         assertTrue(score > ConnectedScore.WIFI_TRANSITION_SCORE);
    209     }
    210 
    211     /**
    212      * Bad RSSI without data moving should allow handoff
    213      *
    214      * The setup has a bad RSSI, and the txSuccessRate is below threshold; several
    215      * scoring iterations are performed.
    216      *
    217      * Expect the score to drop below the handoff threshold.
    218      */
    219     @Test
    220     public void giveUpOnBadRssiWhenDataIsNotMoving() throws Exception {
    221         mWifiInfo.setRssi(-100);
    222         mWifiInfo.setLinkSpeed(6); // Mbps
    223         mWifiInfo.setFrequency(5220);
    224         mWifiScoreReport.enableVerboseLogging(true);
    225         mWifiInfo.txSuccessRate = 0.1;
    226         mWifiInfo.rxSuccessRate = 0.1;
    227         for (int i = 0; i < 10; i++) {
    228             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    229         }
    230         int score = mWifiInfo.score;
    231         assertTrue(score < ConnectedScore.WIFI_TRANSITION_SCORE);
    232         verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
    233     }
    234 
    235     /**
    236      * When the score ramps down to the exit theshold, let go.
    237      */
    238     @Test
    239     public void giveUpOnBadRssiAggressively() throws Exception {
    240         String oops = "giveUpOnBadRssiAggressively";
    241         mWifiInfo.setFrequency(5220);
    242         for (int rssi = -60; rssi >= -83; rssi -= 1) {
    243             mWifiInfo.setRssi(rssi);
    244             oops += " " + mClock.mWallClockMillis + "," + rssi;
    245             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    246             oops += ":" + mWifiInfo.score;
    247         }
    248         int score = mWifiInfo.score;
    249         verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
    250         assertTrue(oops, score < ConnectedScore.WIFI_TRANSITION_SCORE);
    251     }
    252 
    253     /**
    254      * RSSI that falls rapidly but does not cross entry threshold should not cause handoff
    255      *
    256      * Expect the score to not drop below the handoff threshold.
    257      */
    258     @Test
    259     public void stayOnIfRssiDoesNotGetBelowEntryThreshold() throws Exception {
    260         String oops = "didNotStickLanding";
    261         int minScore = 100;
    262         mWifiInfo.setLinkSpeed(6); // Mbps
    263         mWifiInfo.setFrequency(5220);
    264         mWifiScoreReport.enableVerboseLogging(true);
    265         mWifiInfo.txSuccessRate = 0.1;
    266         mWifiInfo.rxSuccessRate = 0.1;
    267         assertTrue(mScoringParams.update("rssi5=-83:-80:-66:-55"));
    268         for (int r = -30; r >= -100; r -= 1) {
    269             int rssi = Math.max(r, -80);
    270             mWifiInfo.setRssi(rssi);
    271             oops += " " + mClock.mWallClockMillis + "," + rssi;
    272             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    273             oops += ":" + mWifiInfo.score;
    274             if (mWifiInfo.score < minScore) minScore = mWifiInfo.score;
    275         }
    276         assertTrue(oops, minScore > ConnectedScore.WIFI_TRANSITION_SCORE);
    277     }
    278 
    279     /**
    280      * Don't breach if the success rates are great
    281      *
    282      * Ramp the RSSI down, but maintain a high packet throughput
    283      *
    284      * Expect score to stay above above threshold.
    285      */
    286     @Test
    287     public void allowTerribleRssiIfDataIsMovingWell() throws Exception {
    288         mWifiInfo.txSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
    289         mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
    290         assertTrue(mWifiInfo.txSuccessRate > 10);
    291         mWifiInfo.setFrequency(5220);
    292         for (int r = -30; r >= -120; r -= 2) {
    293             mWifiInfo.setRssi(r);
    294             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    295             assertTrue(mWifiInfo.score > ConnectedScore.WIFI_TRANSITION_SCORE);
    296         }
    297         // If the throughput dips, we should let go
    298         mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() - 0.1;
    299         mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    300         assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
    301         // And even if throughput improves again, once we have decided to let go, disregard
    302         // the good rates.
    303         mWifiInfo.rxSuccessRate = mScoringParams.getYippeeSkippyPacketsPerSecond() + 0.1;
    304         mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    305         assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
    306     }
    307 
    308     /**
    309      * Never ask for nud check when nud=0
    310      */
    311     @Test
    312     public void neverAskForNudCheckWhenNudKnobIsZero() throws Exception {
    313         assertTrue(mScoringParams.update("nud=0"));
    314         assertEquals(0, mScoringParams.getNudKnob());
    315         mWifiInfo.setFrequency(5220);
    316         for (int rssi = -30; rssi >= -120; rssi -= 1) {
    317             mWifiInfo.setRssi(rssi);
    318             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    319             assertFalse(mWifiScoreReport.shouldCheckIpLayer());
    320         }
    321     }
    322 
    323     /**
    324      * Eventually ask for nud check when nud=1
    325      */
    326     @Test
    327     public void eventuallyAskForNudCheckWhenNudKnobIsOne() throws Exception {
    328         String oops = "nud=1";
    329         long lastAskedMillis = 0; // Check that we don't send too soon
    330         int asks = 0; // Keep track of how many time we asked
    331         assertTrue(mScoringParams.update("nud=1"));
    332         assertEquals(1, mScoringParams.getNudKnob());
    333         mWifiInfo.setFrequency(5220);
    334         for (int rssi = -40; rssi >= -120; rssi -= 1) {
    335             mWifiInfo.setRssi(rssi);
    336             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    337             boolean ask = mWifiScoreReport.shouldCheckIpLayer();
    338             if (ask) {
    339                 assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
    340                 assertTrue(oops, mClock.mWallClockMillis >= lastAskedMillis + 5000);
    341                 lastAskedMillis = mClock.mWallClockMillis;
    342                 oops += " " + lastAskedMillis + ":" + mWifiInfo.score;
    343                 mWifiScoreReport.noteIpCheck();
    344                 asks++;
    345             }
    346         }
    347         assertTrue(oops + " asks:" + asks, asks > 5 && asks < 12);
    348     }
    349 
    350 
    351     /**
    352      * Ask for more nud checks when nud=10
    353      */
    354     @Test
    355     public void askForMoreNudChecksWhenNudKnobIsBigger() throws Exception {
    356         String oops = "nud=10";
    357         long lastAskedMillis = 0; // Check that we don't send too soon
    358         int asks = 0; // Keep track of how many time we asked
    359         assertTrue(mScoringParams.update("nud=10"));
    360         assertEquals(10, mScoringParams.getNudKnob());
    361         mWifiInfo.setFrequency(5220);
    362         for (int rssi = -40; rssi >= -120; rssi -= 1) {
    363             mWifiInfo.setRssi(rssi);
    364             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    365             boolean ask = mWifiScoreReport.shouldCheckIpLayer();
    366             if (ask) {
    367                 assertTrue(mWifiInfo.score < ConnectedScore.WIFI_TRANSITION_SCORE);
    368                 assertTrue(oops, mClock.mWallClockMillis >= lastAskedMillis + 5000);
    369                 lastAskedMillis = mClock.mWallClockMillis;
    370                 oops += " " + lastAskedMillis + ":" + mWifiInfo.score;
    371                 mWifiScoreReport.noteIpCheck();
    372                 asks++;
    373             }
    374         }
    375         assertTrue(oops + " asks:" + asks, asks > 12 && asks < 80);
    376     }
    377 
    378     /**
    379      * Test initial conditions, and after reset()
    380      */
    381     @Test
    382     public void exerciseReset() throws Exception {
    383         assertFalse(mWifiScoreReport.shouldCheckIpLayer());
    384         mWifiScoreReport.reset();
    385         assertFalse(mWifiScoreReport.shouldCheckIpLayer());
    386     }
    387 
    388     /**
    389      * This setup causes some reports to be generated when println
    390      * methods are called, to check for "concurrent" modification
    391      * errors.
    392      */
    393     private void setupToGenerateAReportWhenPrintlnIsCalled() {
    394         int[] counter = new int[1];
    395         doAnswer(answerVoid((String line) -> {
    396             if (counter[0]++ < 3) {
    397                 mWifiScoreReport.calculateAndReportScore(
    398                         mWifiInfo, mNetworkAgent, mWifiMetrics);
    399             }
    400         })).when(mPrintWriter).println(anyString());
    401     }
    402 
    403     /**
    404      * Test data logging
    405      */
    406     @Test
    407     public void testDataLogging() throws Exception {
    408         for (int i = 0; i < 10; i++) {
    409             mWifiInfo.setRssi(-65 + i);
    410             mWifiInfo.setLinkSpeed(300);
    411             mWifiInfo.setFrequency(5220);
    412             mWifiInfo.txSuccessRate = 0.1 + i;
    413             mWifiInfo.txRetriesRate = 0.2 + i;
    414             mWifiInfo.txBadRate = 0.01 * i;
    415             mWifiInfo.rxSuccessRate = 0.3 + i;
    416             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    417         }
    418         setupToGenerateAReportWhenPrintlnIsCalled();
    419         mWifiScoreReport.dump(null, mPrintWriter, null);
    420         verify(mPrintWriter, times(11)).println(anyString());
    421     }
    422 
    423     /**
    424      *  Test data logging limit
    425      *  <p>
    426      *  Check that only a bounded amount of data is collected for dumpsys report
    427      */
    428     @Test
    429     public void testDataLoggingLimit() throws Exception {
    430         for (int i = 0; i < 3620; i++) {
    431             mWifiInfo.setRssi(-65 + i % 20);
    432             mWifiInfo.setLinkSpeed(300);
    433             mWifiInfo.setFrequency(5220);
    434             mWifiInfo.txSuccessRate = 0.1 + i % 100;
    435             mWifiInfo.txRetriesRate = 0.2 + i % 100;
    436             mWifiInfo.txBadRate = 0.0001 * i;
    437             mWifiInfo.rxSuccessRate = 0.3 + i % 200;
    438             mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
    439         }
    440         mWifiScoreReport.dump(null, mPrintWriter, null);
    441         verify(mPrintWriter, atMost(3601)).println(anyString());
    442     }
    443 }
    444