WifiConnectivityManagerTest.java revision fb196453c07daad5e525520cecad84cec5d89fb7
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 17package com.android.server.wifi; 18 19import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; 20 21import static org.junit.Assert.*; 22import static org.mockito.Mockito.*; 23 24import android.content.Context; 25import android.content.res.Resources; 26import android.net.wifi.ScanResult; 27import android.net.wifi.ScanResult.InformationElement; 28import android.net.wifi.SupplicantState; 29import android.net.wifi.WifiConfiguration; 30import android.net.wifi.WifiInfo; 31import android.net.wifi.WifiManager; 32import android.net.wifi.WifiScanner; 33import android.net.wifi.WifiScanner.PnoScanListener; 34import android.net.wifi.WifiScanner.PnoSettings; 35import android.net.wifi.WifiScanner.ScanListener; 36import android.net.wifi.WifiScanner.ScanSettings; 37import android.net.wifi.WifiSsid; 38import android.os.WorkSource; 39import android.test.suitebuilder.annotation.SmallTest; 40 41import com.android.internal.R; 42import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; 43 44import org.junit.After; 45import org.junit.Before; 46import org.junit.Test; 47 48import java.nio.charset.StandardCharsets; 49import java.util.ArrayList; 50import java.util.HashSet; 51import java.util.concurrent.atomic.AtomicInteger; 52 53/** 54 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}. 55 */ 56@SmallTest 57public class WifiConnectivityManagerTest { 58 59 /** 60 * Called before each test 61 */ 62 @Before 63 public void setUp() throws Exception { 64 mWifiInjector = mockWifiInjector(); 65 mResource = mockResource(); 66 mAlarmManager = new MockAlarmManager(); 67 mContext = mockContext(); 68 mWifiStateMachine = mockWifiStateMachine(); 69 mWifiConfigManager = mockWifiConfigManager(); 70 mWifiInfo = getWifiInfo(); 71 mWifiScanner = mockWifiScanner(); 72 mWifiQNS = mockWifiQualifiedNetworkSelector(); 73 mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine, 74 mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector, 75 mLooper.getLooper()); 76 mWifiConnectivityManager.setWifiEnabled(true); 77 when(mClock.currentTimeMillis()).thenReturn(System.currentTimeMillis()); 78 } 79 80 /** 81 * Called after each test 82 */ 83 @After 84 public void cleanup() { 85 validateMockitoUsage(); 86 } 87 88 private Resources mResource; 89 private Context mContext; 90 private MockAlarmManager mAlarmManager; 91 private MockLooper mLooper = new MockLooper(); 92 private WifiConnectivityManager mWifiConnectivityManager; 93 private WifiQualifiedNetworkSelector mWifiQNS; 94 private WifiStateMachine mWifiStateMachine; 95 private WifiScanner mWifiScanner; 96 private WifiConfigManager mWifiConfigManager; 97 private WifiInfo mWifiInfo; 98 private Clock mClock = mock(Clock.class); 99 private WifiLastResortWatchdog mWifiLastResortWatchdog; 100 private WifiMetrics mWifiMetrics; 101 private WifiInjector mWifiInjector; 102 103 private static final int CANDIDATE_NETWORK_ID = 0; 104 private static final String CANDIDATE_SSID = "\"AnSsid\""; 105 private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3"; 106 private static final String TAG = "WifiConnectivityManager Unit Test"; 107 private static final long CURRENT_SYSTEM_TIME_MS = 1000; 108 109 Resources mockResource() { 110 Resources resource = mock(Resources.class); 111 112 when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80); 113 when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24); 114 115 return resource; 116 } 117 118 Context mockContext() { 119 Context context = mock(Context.class); 120 121 when(context.getResources()).thenReturn(mResource); 122 when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( 123 mAlarmManager.getAlarmManager()); 124 125 return context; 126 } 127 128 WifiScanner mockWifiScanner() { 129 WifiScanner scanner = mock(WifiScanner.class); 130 131 // dummy scan results. QNS PeriodicScanListener bulids scanDetails from 132 // the fullScanResult and doesn't really use results 133 final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1]; 134 135 // do a synchronous answer for the ScanListener callbacks 136 doAnswer(new AnswerWithArguments() { 137 public void answer(ScanSettings settings, ScanListener listener, 138 WorkSource workSource) throws Exception { 139 listener.onResults(scanDatas); 140 }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject()); 141 142 doAnswer(new AnswerWithArguments() { 143 public void answer(ScanSettings settings, ScanListener listener, 144 WorkSource workSource) throws Exception { 145 listener.onResults(scanDatas); 146 }}).when(scanner).startScan(anyObject(), anyObject(), anyObject()); 147 148 // This unfortunately needs to be a somewhat valid scan result, otherwise 149 // |ScanDetailUtil.toScanDetail| raises exceptions. 150 final ScanResult[] scanResults = new ScanResult[1]; 151 scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID), 152 CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", 153 -78, 2450, 1025, 22, 33, 20, 0, 0, true); 154 scanResults[0].informationElements = new InformationElement[1]; 155 scanResults[0].informationElements[0] = new InformationElement(); 156 scanResults[0].informationElements[0].id = InformationElement.EID_SSID; 157 scanResults[0].informationElements[0].bytes = 158 CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8); 159 160 doAnswer(new AnswerWithArguments() { 161 public void answer(ScanSettings settings, PnoSettings pnoSettings, 162 PnoScanListener listener) throws Exception { 163 listener.onPnoNetworkFound(scanResults); 164 }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject()); 165 166 doAnswer(new AnswerWithArguments() { 167 public void answer(ScanSettings settings, PnoSettings pnoSettings, 168 PnoScanListener listener) throws Exception { 169 listener.onPnoNetworkFound(scanResults); 170 }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject()); 171 172 return scanner; 173 } 174 175 WifiStateMachine mockWifiStateMachine() { 176 WifiStateMachine stateMachine = mock(WifiStateMachine.class); 177 178 when(stateMachine.getFrequencyBand()).thenReturn(1); 179 when(stateMachine.isLinkDebouncing()).thenReturn(false); 180 when(stateMachine.isConnected()).thenReturn(false); 181 when(stateMachine.isDisconnected()).thenReturn(true); 182 when(stateMachine.isSupplicantTransientState()).thenReturn(false); 183 184 return stateMachine; 185 } 186 187 WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() { 188 WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class); 189 190 WifiConfiguration candidate = generateWifiConfig( 191 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null); 192 candidate.BSSID = CANDIDATE_BSSID; 193 ScanResult candidateScanResult = new ScanResult(); 194 candidateScanResult.SSID = CANDIDATE_SSID; 195 candidateScanResult.BSSID = CANDIDATE_BSSID; 196 candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); 197 198 when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(), 199 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate); 200 return qns; 201 } 202 203 WifiInfo getWifiInfo() { 204 WifiInfo wifiInfo = new WifiInfo(); 205 206 wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); 207 wifiInfo.setBSSID(null); 208 wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED); 209 210 return wifiInfo; 211 } 212 213 WifiConfigManager mockWifiConfigManager() { 214 WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); 215 216 when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null); 217 wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger( 218 WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); 219 wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger( 220 WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD); 221 222 // Pass dummy pno network list, otherwise Pno scan requests will not be triggered. 223 PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID); 224 ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>(); 225 pnoNetworkList.add(pnoNetwork); 226 when(wifiConfigManager.retrieveDisconnectedPnoNetworkList()).thenReturn(pnoNetworkList); 227 when(wifiConfigManager.retrieveConnectedPnoNetworkList()).thenReturn(pnoNetworkList); 228 229 return wifiConfigManager; 230 } 231 232 WifiInjector mockWifiInjector() { 233 WifiInjector wifiInjector = mock(WifiInjector.class); 234 mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class); 235 mWifiMetrics = mock(WifiMetrics.class); 236 when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); 237 when(wifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); 238 when(wifiInjector.getClock()).thenReturn(mClock); 239 return wifiInjector; 240 } 241 242 /** 243 * Wifi enters disconnected state while screen is on. 244 * 245 * Expected behavior: WifiConnectivityManager calls 246 * WifiStateMachine.autoConnectToNetwork() with the 247 * expected candidate network ID and BSSID. 248 */ 249 @Test 250 public void enterWifiDisconnectedStateWhenScreenOn() { 251 // Set screen to on 252 mWifiConnectivityManager.handleScreenStateChanged(true); 253 254 // Set WiFi to disconnected state 255 mWifiConnectivityManager.handleConnectionStateChanged( 256 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 257 258 verify(mWifiStateMachine).autoConnectToNetwork( 259 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 260 } 261 262 /** 263 * Wifi enters connected state while screen is on. 264 * 265 * Expected behavior: WifiConnectivityManager calls 266 * WifiStateMachine.autoConnectToNetwork() with the 267 * expected candidate network ID and BSSID. 268 */ 269 @Test 270 public void enterWifiConnectedStateWhenScreenOn() { 271 // Set screen to on 272 mWifiConnectivityManager.handleScreenStateChanged(true); 273 274 // Set WiFi to connected state 275 mWifiConnectivityManager.handleConnectionStateChanged( 276 WifiConnectivityManager.WIFI_STATE_CONNECTED); 277 278 verify(mWifiStateMachine).autoConnectToNetwork( 279 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 280 } 281 282 /** 283 * Screen turned on while WiFi in disconnected state. 284 * 285 * Expected behavior: WifiConnectivityManager calls 286 * WifiStateMachine.autoConnectToNetwork() with the 287 * expected candidate network ID and BSSID. 288 */ 289 @Test 290 public void turnScreenOnWhenWifiInDisconnectedState() { 291 // Set WiFi to disconnected state 292 mWifiConnectivityManager.handleConnectionStateChanged( 293 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 294 295 // Set screen to on 296 mWifiConnectivityManager.handleScreenStateChanged(true); 297 298 verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork( 299 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 300 } 301 302 /** 303 * Screen turned on while WiFi in connected state. 304 * 305 * Expected behavior: WifiConnectivityManager calls 306 * WifiStateMachine.autoConnectToNetwork() with the 307 * expected candidate network ID and BSSID. 308 */ 309 @Test 310 public void turnScreenOnWhenWifiInConnectedState() { 311 // Set WiFi to connected state 312 mWifiConnectivityManager.handleConnectionStateChanged( 313 WifiConnectivityManager.WIFI_STATE_CONNECTED); 314 315 // Set screen to on 316 mWifiConnectivityManager.handleScreenStateChanged(true); 317 318 verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork( 319 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 320 } 321 322 /** 323 * Multiple back to back connection attempts within the rate interval should be rate limited. 324 * 325 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 326 * with the expected candidate network ID and BSSID for only the expected number of times within 327 * the given interval. 328 */ 329 @Test 330 public void connectionAttemptRateLimitedWhenScreenOff() { 331 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 332 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 333 int numAttempts = 0; 334 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 335 336 mWifiConnectivityManager.handleScreenStateChanged(false); 337 338 // First attempt the max rate number of connections within the rate interval. 339 long currentTimeStamp = 0; 340 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 341 currentTimeStamp += connectionAttemptIntervals; 342 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 343 // Set WiFi to disconnected state to trigger PNO scan 344 mWifiConnectivityManager.handleConnectionStateChanged( 345 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 346 numAttempts++; 347 } 348 // Now trigger another connection attempt before the rate interval, this should be 349 // skipped because we've crossed rate limit. 350 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 351 // Set WiFi to disconnected state to trigger PNO scan 352 mWifiConnectivityManager.handleConnectionStateChanged( 353 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 354 355 // Verify that we attempt to connect upto the rate. 356 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 357 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 358 } 359 360 /** 361 * Multiple back to back connection attempts outside the rate interval should not be rate 362 * limited. 363 * 364 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 365 * with the expected candidate network ID and BSSID for only the expected number of times within 366 * the given interval. 367 */ 368 @Test 369 public void connectionAttemptNotRateLimitedWhenScreenOff() { 370 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 371 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 372 int numAttempts = 0; 373 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 374 375 mWifiConnectivityManager.handleScreenStateChanged(false); 376 377 // First attempt the max rate number of connections within the rate interval. 378 long currentTimeStamp = 0; 379 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 380 currentTimeStamp += connectionAttemptIntervals; 381 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 382 // Set WiFi to disconnected state to trigger PNO scan 383 mWifiConnectivityManager.handleConnectionStateChanged( 384 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 385 numAttempts++; 386 } 387 // Now trigger another connection attempt after the rate interval, this should not be 388 // skipped because we should've evicted the older attempt. 389 when(mClock.currentTimeMillis()).thenReturn( 390 currentTimeStamp + connectionAttemptIntervals * 2); 391 // Set WiFi to disconnected state to trigger PNO scan 392 mWifiConnectivityManager.handleConnectionStateChanged( 393 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 394 numAttempts++; 395 396 // Verify that all the connection attempts went through 397 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 398 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 399 } 400 401 /** 402 * Multiple back to back connection attempts after a user selection should not be rate limited. 403 * 404 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 405 * with the expected candidate network ID and BSSID for only the expected number of times within 406 * the given interval. 407 */ 408 @Test 409 public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() { 410 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 411 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 412 int numAttempts = 0; 413 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 414 415 mWifiConnectivityManager.handleScreenStateChanged(false); 416 417 // First attempt the max rate number of connections within the rate interval. 418 long currentTimeStamp = 0; 419 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 420 currentTimeStamp += connectionAttemptIntervals; 421 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 422 // Set WiFi to disconnected state to trigger PNO scan 423 mWifiConnectivityManager.handleConnectionStateChanged( 424 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 425 numAttempts++; 426 } 427 428 mWifiConnectivityManager.connectToUserSelectNetwork(CANDIDATE_NETWORK_ID, false); 429 430 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 431 currentTimeStamp += connectionAttemptIntervals; 432 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 433 // Set WiFi to disconnected state to trigger PNO scan 434 mWifiConnectivityManager.handleConnectionStateChanged( 435 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 436 numAttempts++; 437 } 438 439 // Verify that all the connection attempts went through 440 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 441 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 442 } 443 444 /** 445 * PNO retry for low RSSI networks. 446 * 447 * Expected behavior: WifiConnectivityManager doubles the low RSSI 448 * network retry delay value after QNS skips the PNO scan results 449 * because of their low RSSI values. 450 */ 451 @Test 452 public void PnoRetryForLowRssiNetwork() { 453 when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(), 454 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null); 455 456 // Set screen to off 457 mWifiConnectivityManager.handleScreenStateChanged(false); 458 459 // Get the current retry delay value 460 int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager 461 .getLowRssiNetworkRetryDelay(); 462 463 // Set WiFi to disconnected state to trigger PNO scan 464 mWifiConnectivityManager.handleConnectionStateChanged( 465 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 466 467 // Get the retry delay value after QNS didn't select a 468 // network candicate from the PNO scan results. 469 int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager 470 .getLowRssiNetworkRetryDelay(); 471 472 assertEquals(lowRssiNetworkRetryDelayStartValue * 2, 473 lowRssiNetworkRetryDelayAfterPnoValue); 474 } 475 476 /** 477 * Ensure that the watchdog bite increments the "Pno bad" metric. 478 * 479 * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find 480 * a candidate while watchdog single scan did. 481 */ 482 @Test 483 public void watchdogBitePnoBadIncrementsMetrics() { 484 // Set screen to off 485 mWifiConnectivityManager.handleScreenStateChanged(false); 486 487 // Set WiFi to disconnected state to trigger PNO scan 488 mWifiConnectivityManager.handleConnectionStateChanged( 489 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 490 491 // Now fire the watchdog alarm and verify the metrics were incremented. 492 mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); 493 mLooper.dispatchAll(); 494 495 verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad(); 496 verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood(); 497 } 498 499 /** 500 * Ensure that the watchdog bite increments the "Pno good" metric. 501 * 502 * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find 503 * a candidate which was the same with watchdog single scan. 504 */ 505 @Test 506 public void watchdogBitePnoGoodIncrementsMetrics() { 507 // Qns returns no candidate after watchdog single scan. 508 when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(), 509 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null); 510 511 // Set screen to off 512 mWifiConnectivityManager.handleScreenStateChanged(false); 513 514 // Set WiFi to disconnected state to trigger PNO scan 515 mWifiConnectivityManager.handleConnectionStateChanged( 516 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 517 518 // Now fire the watchdog alarm and verify the metrics were incremented. 519 mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); 520 mLooper.dispatchAll(); 521 522 verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood(); 523 verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad(); 524 } 525 526 /** 527 * Verify that scan interval for screen on and wifi disconnected scenario 528 * is in the exponential backoff fashion. 529 * 530 * Expected behavior: WifiConnectivityManager doubles periodic 531 * scan interval. 532 */ 533 @Test 534 public void checkPeriodicScanIntervalWhenDisconnected() { 535 when(mClock.currentTimeMillis()).thenReturn(CURRENT_SYSTEM_TIME_MS); 536 537 // Set screen to ON 538 mWifiConnectivityManager.handleScreenStateChanged(true); 539 540 // Set WiFi to disconnected state to trigger periodic scan 541 mWifiConnectivityManager.handleConnectionStateChanged( 542 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 543 544 // Get the first periodic scan interval 545 long firstIntervalMs = mAlarmManager 546 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 547 - CURRENT_SYSTEM_TIME_MS; 548 assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS); 549 550 // Now fire the first periodic scan alarm timer 551 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 552 mLooper.dispatchAll(); 553 554 // Get the second periodic scan interval 555 long secondIntervalMs = mAlarmManager 556 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 557 - CURRENT_SYSTEM_TIME_MS; 558 559 // Verify the intervals are exponential back off 560 assertEquals(firstIntervalMs * 2, secondIntervalMs); 561 562 // Make sure we eventually stay at the maximum scan interval. 563 long intervalMs = 0; 564 for (int i = 0; i < 5; i++) { 565 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 566 mLooper.dispatchAll(); 567 intervalMs = mAlarmManager 568 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 569 - CURRENT_SYSTEM_TIME_MS; 570 } 571 572 assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS); 573 } 574 575 /** 576 * Verify that scan interval for screen on and wifi connected scenario 577 * is in the exponential backoff fashion. 578 * 579 * Expected behavior: WifiConnectivityManager doubles periodic 580 * scan interval. 581 */ 582 @Test 583 public void checkPeriodicScanIntervalWhenConnected() { 584 when(mClock.currentTimeMillis()).thenReturn(CURRENT_SYSTEM_TIME_MS); 585 586 // Set screen to ON 587 mWifiConnectivityManager.handleScreenStateChanged(true); 588 589 // Set WiFi to connected state to trigger periodic scan 590 mWifiConnectivityManager.handleConnectionStateChanged( 591 WifiConnectivityManager.WIFI_STATE_CONNECTED); 592 593 // Get the first periodic scan interval 594 long firstIntervalMs = mAlarmManager 595 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 596 - CURRENT_SYSTEM_TIME_MS; 597 assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS); 598 599 // Now fire the first periodic scan alarm timer 600 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 601 mLooper.dispatchAll(); 602 603 // Get the second periodic scan interval 604 long secondIntervalMs = mAlarmManager 605 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 606 - CURRENT_SYSTEM_TIME_MS; 607 608 // Verify the intervals are exponential back off 609 assertEquals(firstIntervalMs * 2, secondIntervalMs); 610 611 // Make sure we eventually stay at the maximum scan interval. 612 long intervalMs = 0; 613 for (int i = 0; i < 5; i++) { 614 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 615 mLooper.dispatchAll(); 616 intervalMs = mAlarmManager 617 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 618 - CURRENT_SYSTEM_TIME_MS; 619 } 620 621 assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS); 622 } 623 624 /** 625 * Verify that we perform full band scan when the currently connected network's tx/rx success 626 * rate is low. 627 * 628 * Expected behavior: WifiConnectivityManager does full band scan. 629 */ 630 @Test 631 public void checkSingleScanSettingsWhenConnectedWithLowDataRate() { 632 mWifiInfo.txSuccessRate = 0; 633 mWifiInfo.rxSuccessRate = 0; 634 635 final HashSet<Integer> channelList = new HashSet<>(); 636 channelList.add(1); 637 channelList.add(2); 638 channelList.add(3); 639 640 when(mWifiStateMachine.getCurrentWifiConfiguration()) 641 .thenReturn(new WifiConfiguration()); 642 when(mWifiStateMachine.getFrequencyBand()) 643 .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ); 644 when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt())) 645 .thenReturn(channelList); 646 647 doAnswer(new AnswerWithArguments() { 648 public void answer(ScanSettings settings, ScanListener listener, 649 WorkSource workSource) throws Exception { 650 assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS); 651 assertNull(settings.channels); 652 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 653 654 // Set screen to ON 655 mWifiConnectivityManager.handleScreenStateChanged(true); 656 657 // Set WiFi to connected state to trigger periodic scan 658 mWifiConnectivityManager.handleConnectionStateChanged( 659 WifiConnectivityManager.WIFI_STATE_CONNECTED); 660 661 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 662 } 663 664 /** 665 * Verify that we perform partial scan when the currently connected network's tx/rx success 666 * rate is high and when the currently connected network is present in scan 667 * cache in WifiConfigManager. 668 * 669 * Expected behavior: WifiConnectivityManager does full band scan. 670 */ 671 @Test 672 public void checkSingleScanSettingsWhenConnectedWithHighDataRate() { 673 mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2; 674 mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2; 675 676 final HashSet<Integer> channelList = new HashSet<>(); 677 channelList.add(1); 678 channelList.add(2); 679 channelList.add(3); 680 681 when(mWifiStateMachine.getCurrentWifiConfiguration()) 682 .thenReturn(new WifiConfiguration()); 683 when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt())) 684 .thenReturn(channelList); 685 686 doAnswer(new AnswerWithArguments() { 687 public void answer(ScanSettings settings, ScanListener listener, 688 WorkSource workSource) throws Exception { 689 assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED); 690 assertEquals(settings.channels.length, channelList.size()); 691 for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) { 692 assertTrue(channelList.contains(settings.channels[chanIdx].frequency)); 693 } 694 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 695 696 // Set screen to ON 697 mWifiConnectivityManager.handleScreenStateChanged(true); 698 699 // Set WiFi to connected state to trigger periodic scan 700 mWifiConnectivityManager.handleConnectionStateChanged( 701 WifiConnectivityManager.WIFI_STATE_CONNECTED); 702 703 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 704 } 705 706 /** 707 * Verify that we fall back to full band scan when the currently connected network's tx/rx 708 * success rate is high and the currently connected network is not present in scan cache in 709 * WifiConfigManager. This is simulated by returning an empty hashset in |makeChannelList|. 710 * 711 * Expected behavior: WifiConnectivityManager does full band scan. 712 */ 713 @Test 714 public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() { 715 mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2; 716 mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2; 717 718 final HashSet<Integer> channelList = new HashSet<>(); 719 720 when(mWifiStateMachine.getCurrentWifiConfiguration()) 721 .thenReturn(new WifiConfiguration()); 722 when(mWifiStateMachine.getFrequencyBand()) 723 .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ); 724 when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt())) 725 .thenReturn(channelList); 726 727 doAnswer(new AnswerWithArguments() { 728 public void answer(ScanSettings settings, ScanListener listener, 729 WorkSource workSource) throws Exception { 730 assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS); 731 assertNull(settings.channels); 732 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 733 734 // Set screen to ON 735 mWifiConnectivityManager.handleScreenStateChanged(true); 736 737 // Set WiFi to connected state to trigger periodic scan 738 mWifiConnectivityManager.handleConnectionStateChanged( 739 WifiConnectivityManager.WIFI_STATE_CONNECTED); 740 741 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 742 } 743} 744