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