WifiConnectivityManagerTest.java revision 2dfadf313b3e5194d56d2fa24b9270086cda165a
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; 20import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; 21 22import static org.junit.Assert.*; 23import static org.mockito.Mockito.*; 24 25import android.app.test.MockAnswerUtil.AnswerWithArguments; 26import android.app.test.TestAlarmManager; 27import android.content.Context; 28import android.content.res.Resources; 29import android.net.wifi.ScanResult; 30import android.net.wifi.ScanResult.InformationElement; 31import android.net.wifi.SupplicantState; 32import android.net.wifi.WifiConfiguration; 33import android.net.wifi.WifiInfo; 34import android.net.wifi.WifiScanner; 35import android.net.wifi.WifiScanner.PnoScanListener; 36import android.net.wifi.WifiScanner.PnoSettings; 37import android.net.wifi.WifiScanner.ScanListener; 38import android.net.wifi.WifiScanner.ScanSettings; 39import android.net.wifi.WifiSsid; 40import android.os.SystemClock; 41import android.os.WorkSource; 42import android.os.test.TestLooper; 43import android.test.suitebuilder.annotation.SmallTest; 44 45import com.android.internal.R; 46 47import org.junit.After; 48import org.junit.Before; 49import org.junit.Test; 50import org.mockito.ArgumentCaptor; 51 52import java.nio.charset.StandardCharsets; 53import java.util.ArrayList; 54import java.util.HashSet; 55 56/** 57 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}. 58 */ 59@SmallTest 60public class WifiConnectivityManagerTest { 61 62 /** 63 * Called before each test 64 */ 65 @Before 66 public void setUp() throws Exception { 67 mWifiInjector = mockWifiInjector(); 68 mResource = mockResource(); 69 mAlarmManager = new TestAlarmManager(); 70 mContext = mockContext(); 71 mWifiStateMachine = mockWifiStateMachine(); 72 mWifiConfigManager = mockWifiConfigManager(); 73 mWifiInfo = getWifiInfo(); 74 mWifiScanner = mockWifiScanner(); 75 mWifiNS = mockWifiNetworkSelector(); 76 mWifiConnectivityManager = createConnectivityManager(); 77 mWifiConnectivityManager.setWifiEnabled(true); 78 when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); 79 } 80 81 /** 82 * Called after each test 83 */ 84 @After 85 public void cleanup() { 86 validateMockitoUsage(); 87 } 88 89 private Resources mResource; 90 private Context mContext; 91 private TestAlarmManager mAlarmManager; 92 private TestLooper mLooper = new TestLooper(); 93 private WifiConnectivityManager mWifiConnectivityManager; 94 private WifiNetworkSelector mWifiNS; 95 private WifiStateMachine mWifiStateMachine; 96 private WifiScanner mWifiScanner; 97 private WifiConfigManager mWifiConfigManager; 98 private WifiInfo mWifiInfo; 99 private Clock mClock = mock(Clock.class); 100 private WifiLastResortWatchdog mWifiLastResortWatchdog; 101 private WifiMetrics mWifiMetrics; 102 private WifiInjector mWifiInjector; 103 private MockResources mResources; 104 105 private static final int CANDIDATE_NETWORK_ID = 0; 106 private static final String CANDIDATE_SSID = "\"AnSsid\""; 107 private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3"; 108 private static final String TAG = "WifiConnectivityManager Unit Test"; 109 private static final long CURRENT_SYSTEM_TIME_MS = 1000; 110 111 Resources mockResource() { 112 Resources resource = mock(Resources.class); 113 114 when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80); 115 when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24); 116 when(resource.getBoolean( 117 R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); 118 when(resource.getInteger( 119 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) 120 .thenReturn(-60); 121 when(resource.getInteger( 122 R.integer.config_wifi_framework_current_network_boost)) 123 .thenReturn(16); 124 return resource; 125 } 126 127 Context mockContext() { 128 Context context = mock(Context.class); 129 130 when(context.getResources()).thenReturn(mResource); 131 when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( 132 mAlarmManager.getAlarmManager()); 133 134 return context; 135 } 136 137 WifiScanner mockWifiScanner() { 138 WifiScanner scanner = mock(WifiScanner.class); 139 ArgumentCaptor<ScanListener> allSingleScanListenerCaptor = 140 ArgumentCaptor.forClass(ScanListener.class); 141 142 doNothing().when(scanner).registerScanListener(allSingleScanListenerCaptor.capture()); 143 144 // dummy scan results. QNS PeriodicScanListener bulids scanDetails from 145 // the fullScanResult and doesn't really use results 146 final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1]; 147 148 // do a synchronous answer for the ScanListener callbacks 149 doAnswer(new AnswerWithArguments() { 150 public void answer(ScanSettings settings, ScanListener listener, 151 WorkSource workSource) throws Exception { 152 listener.onResults(scanDatas); 153 }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject()); 154 155 doAnswer(new AnswerWithArguments() { 156 public void answer(ScanSettings settings, ScanListener listener, 157 WorkSource workSource) throws Exception { 158 listener.onResults(scanDatas); 159 allSingleScanListenerCaptor.getValue().onResults(scanDatas); 160 }}).when(scanner).startScan(anyObject(), anyObject(), anyObject()); 161 162 // This unfortunately needs to be a somewhat valid scan result, otherwise 163 // |ScanDetailUtil.toScanDetail| raises exceptions. 164 final ScanResult[] scanResults = new ScanResult[1]; 165 scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID), 166 CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", 167 -78, 2450, 1025, 22, 33, 20, 0, 0, true); 168 scanResults[0].informationElements = new InformationElement[1]; 169 scanResults[0].informationElements[0] = new InformationElement(); 170 scanResults[0].informationElements[0].id = InformationElement.EID_SSID; 171 scanResults[0].informationElements[0].bytes = 172 CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8); 173 174 doAnswer(new AnswerWithArguments() { 175 public void answer(ScanSettings settings, PnoSettings pnoSettings, 176 PnoScanListener listener) throws Exception { 177 listener.onPnoNetworkFound(scanResults); 178 }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject()); 179 180 doAnswer(new AnswerWithArguments() { 181 public void answer(ScanSettings settings, PnoSettings pnoSettings, 182 PnoScanListener listener) throws Exception { 183 listener.onPnoNetworkFound(scanResults); 184 }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject()); 185 186 return scanner; 187 } 188 189 WifiStateMachine mockWifiStateMachine() { 190 WifiStateMachine stateMachine = mock(WifiStateMachine.class); 191 192 when(stateMachine.isLinkDebouncing()).thenReturn(false); 193 when(stateMachine.isConnected()).thenReturn(false); 194 when(stateMachine.isDisconnected()).thenReturn(true); 195 when(stateMachine.isSupplicantTransientState()).thenReturn(false); 196 197 return stateMachine; 198 } 199 200 WifiNetworkSelector mockWifiNetworkSelector() { 201 WifiNetworkSelector ns = mock(WifiNetworkSelector.class); 202 203 WifiConfiguration candidate = generateWifiConfig( 204 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null); 205 candidate.BSSID = CANDIDATE_BSSID; 206 ScanResult candidateScanResult = new ScanResult(); 207 candidateScanResult.SSID = CANDIDATE_SSID; 208 candidateScanResult.BSSID = CANDIDATE_BSSID; 209 candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); 210 211 when(ns.selectNetwork(anyObject(), anyObject(), anyBoolean(), anyBoolean(), 212 anyBoolean())).thenReturn(candidate); 213 return ns; 214 } 215 216 WifiInfo getWifiInfo() { 217 WifiInfo wifiInfo = new WifiInfo(); 218 219 wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); 220 wifiInfo.setBSSID(null); 221 wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED); 222 223 return wifiInfo; 224 } 225 226 WifiConfigManager mockWifiConfigManager() { 227 WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); 228 229 when(wifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null); 230 231 // Pass dummy pno network list, otherwise Pno scan requests will not be triggered. 232 PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID); 233 ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>(); 234 pnoNetworkList.add(pnoNetwork); 235 when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList); 236 when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList); 237 238 return wifiConfigManager; 239 } 240 241 WifiInjector mockWifiInjector() { 242 WifiInjector wifiInjector = mock(WifiInjector.class); 243 mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class); 244 mWifiMetrics = mock(WifiMetrics.class); 245 when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); 246 when(wifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); 247 when(wifiInjector.getClock()).thenReturn(mClock); 248 return wifiInjector; 249 } 250 251 WifiConnectivityManager createConnectivityManager() { 252 return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner, 253 mWifiConfigManager, mWifiInfo, mWifiNS, mWifiInjector, mLooper.getLooper(), true); 254 } 255 256 /** 257 * Wifi enters disconnected state while screen is on. 258 * 259 * Expected behavior: WifiConnectivityManager calls 260 * WifiStateMachine.startConnectToNetwork() with the 261 * expected candidate network ID and BSSID. 262 */ 263 @Test 264 public void enterWifiDisconnectedStateWhenScreenOn() { 265 // Set screen to on 266 mWifiConnectivityManager.handleScreenStateChanged(true); 267 268 // Set WiFi to disconnected state 269 mWifiConnectivityManager.handleConnectionStateChanged( 270 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 271 272 verify(mWifiStateMachine).startConnectToNetwork( 273 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 274 } 275 276 /** 277 * Wifi enters connected state while screen is on. 278 * 279 * Expected behavior: WifiConnectivityManager calls 280 * WifiStateMachine.startConnectToNetwork() with the 281 * expected candidate network ID and BSSID. 282 */ 283 @Test 284 public void enterWifiConnectedStateWhenScreenOn() { 285 // Set screen to on 286 mWifiConnectivityManager.handleScreenStateChanged(true); 287 288 // Set WiFi to connected state 289 mWifiConnectivityManager.handleConnectionStateChanged( 290 WifiConnectivityManager.WIFI_STATE_CONNECTED); 291 292 verify(mWifiStateMachine).startConnectToNetwork( 293 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 294 } 295 296 /** 297 * Screen turned on while WiFi in disconnected state. 298 * 299 * Expected behavior: WifiConnectivityManager calls 300 * WifiStateMachine.startConnectToNetwork() with the 301 * expected candidate network ID and BSSID. 302 */ 303 @Test 304 public void turnScreenOnWhenWifiInDisconnectedState() { 305 // Set WiFi to disconnected state 306 mWifiConnectivityManager.handleConnectionStateChanged( 307 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 308 309 // Set screen to on 310 mWifiConnectivityManager.handleScreenStateChanged(true); 311 312 verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork( 313 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 314 } 315 316 /** 317 * Screen turned on while WiFi in connected state. 318 * 319 * Expected behavior: WifiConnectivityManager calls 320 * WifiStateMachine.startConnectToNetwork() with the 321 * expected candidate network ID and BSSID. 322 */ 323 @Test 324 public void turnScreenOnWhenWifiInConnectedState() { 325 // Set WiFi to connected state 326 mWifiConnectivityManager.handleConnectionStateChanged( 327 WifiConnectivityManager.WIFI_STATE_CONNECTED); 328 329 // Set screen to on 330 mWifiConnectivityManager.handleScreenStateChanged(true); 331 332 verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork( 333 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 334 } 335 336 /** 337 * Screen turned on while WiFi in connected state but 338 * auto roaming is disabled. 339 * 340 * Expected behavior: WifiConnectivityManager doesn't invoke 341 * WifiStateMachine.startConnectToNetwork() because roaming 342 * is turned off. 343 */ 344 @Test 345 public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() { 346 // Turn off auto roaming 347 when(mResource.getBoolean( 348 R.bool.config_wifi_framework_enable_associated_network_selection)) 349 .thenReturn(false); 350 mWifiConnectivityManager = createConnectivityManager(); 351 352 // Set WiFi to connected state 353 mWifiConnectivityManager.handleConnectionStateChanged( 354 WifiConnectivityManager.WIFI_STATE_CONNECTED); 355 356 // Set screen to on 357 mWifiConnectivityManager.handleScreenStateChanged(true); 358 359 verify(mWifiStateMachine, times(0)).startConnectToNetwork( 360 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 361 } 362 363 /** 364 * Multiple back to back connection attempts within the rate interval should be rate limited. 365 * 366 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork() 367 * with the expected candidate network ID and BSSID for only the expected number of times within 368 * the given interval. 369 */ 370 @Test 371 public void connectionAttemptRateLimitedWhenScreenOff() { 372 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 373 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 374 int numAttempts = 0; 375 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 376 377 mWifiConnectivityManager.handleScreenStateChanged(false); 378 379 // First attempt the max rate number of connections within the rate interval. 380 long currentTimeStamp = 0; 381 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 382 currentTimeStamp += connectionAttemptIntervals; 383 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 384 // Set WiFi to disconnected state to trigger PNO scan 385 mWifiConnectivityManager.handleConnectionStateChanged( 386 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 387 numAttempts++; 388 } 389 // Now trigger another connection attempt before the rate interval, this should be 390 // skipped because we've crossed rate limit. 391 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 392 // Set WiFi to disconnected state to trigger PNO scan 393 mWifiConnectivityManager.handleConnectionStateChanged( 394 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 395 396 // Verify that we attempt to connect upto the rate. 397 verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( 398 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 399 } 400 401 /** 402 * Multiple back to back connection attempts outside the rate interval should not be rate 403 * limited. 404 * 405 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork() 406 * with the expected candidate network ID and BSSID for only the expected number of times within 407 * the given interval. 408 */ 409 @Test 410 public void connectionAttemptNotRateLimitedWhenScreenOff() { 411 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 412 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 413 int numAttempts = 0; 414 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 415 416 mWifiConnectivityManager.handleScreenStateChanged(false); 417 418 // First attempt the max rate number of connections within the rate interval. 419 long currentTimeStamp = 0; 420 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 421 currentTimeStamp += connectionAttemptIntervals; 422 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 423 // Set WiFi to disconnected state to trigger PNO scan 424 mWifiConnectivityManager.handleConnectionStateChanged( 425 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 426 numAttempts++; 427 } 428 // Now trigger another connection attempt after the rate interval, this should not be 429 // skipped because we should've evicted the older attempt. 430 when(mClock.getElapsedSinceBootMillis()).thenReturn( 431 currentTimeStamp + connectionAttemptIntervals * 2); 432 // Set WiFi to disconnected state to trigger PNO scan 433 mWifiConnectivityManager.handleConnectionStateChanged( 434 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 435 numAttempts++; 436 437 // Verify that all the connection attempts went through 438 verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( 439 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 440 } 441 442 /** 443 * Multiple back to back connection attempts after a user selection should not be rate limited. 444 * 445 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork() 446 * with the expected candidate network ID and BSSID for only the expected number of times within 447 * the given interval. 448 */ 449 @Test 450 public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() { 451 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 452 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 453 int numAttempts = 0; 454 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 455 456 mWifiConnectivityManager.handleScreenStateChanged(false); 457 458 // First attempt the max rate number of connections within the rate interval. 459 long currentTimeStamp = 0; 460 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 461 currentTimeStamp += connectionAttemptIntervals; 462 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 463 // Set WiFi to disconnected state to trigger PNO scan 464 mWifiConnectivityManager.handleConnectionStateChanged( 465 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 466 numAttempts++; 467 } 468 469 mWifiConnectivityManager.setUserConnectChoice(CANDIDATE_NETWORK_ID); 470 471 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 472 currentTimeStamp += connectionAttemptIntervals; 473 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 474 // Set WiFi to disconnected state to trigger PNO scan 475 mWifiConnectivityManager.handleConnectionStateChanged( 476 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 477 numAttempts++; 478 } 479 480 // Verify that all the connection attempts went through 481 verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork( 482 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 483 } 484 485 /** 486 * PNO retry for low RSSI networks. 487 * 488 * Expected behavior: WifiConnectivityManager doubles the low RSSI 489 * network retry delay value after QNS skips the PNO scan results 490 * because of their low RSSI values. 491 */ 492 @Test 493 public void pnoRetryForLowRssiNetwork() { 494 when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyBoolean(), anyBoolean(), 495 anyBoolean())).thenReturn(null); 496 497 // Set screen to off 498 mWifiConnectivityManager.handleScreenStateChanged(false); 499 500 // Get the current retry delay value 501 int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager 502 .getLowRssiNetworkRetryDelay(); 503 504 // Set WiFi to disconnected state to trigger PNO scan 505 mWifiConnectivityManager.handleConnectionStateChanged( 506 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 507 508 // Get the retry delay value after QNS didn't select a 509 // network candicate from the PNO scan results. 510 int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager 511 .getLowRssiNetworkRetryDelay(); 512 513 assertEquals(lowRssiNetworkRetryDelayStartValue * 2, 514 lowRssiNetworkRetryDelayAfterPnoValue); 515 } 516 517 /** 518 * Ensure that the watchdog bite increments the "Pno bad" metric. 519 * 520 * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find 521 * a candidate while watchdog single scan did. 522 */ 523 @Test 524 public void watchdogBitePnoBadIncrementsMetrics() { 525 // Set screen to off 526 mWifiConnectivityManager.handleScreenStateChanged(false); 527 528 // Set WiFi to disconnected state to trigger PNO scan 529 mWifiConnectivityManager.handleConnectionStateChanged( 530 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 531 532 // Now fire the watchdog alarm and verify the metrics were incremented. 533 mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); 534 mLooper.dispatchAll(); 535 536 verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad(); 537 verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood(); 538 } 539 540 /** 541 * Ensure that the watchdog bite increments the "Pno good" metric. 542 * 543 * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find 544 * a candidate which was the same with watchdog single scan. 545 */ 546 @Test 547 public void watchdogBitePnoGoodIncrementsMetrics() { 548 // Qns returns no candidate after watchdog single scan. 549 when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyBoolean(), anyBoolean(), 550 anyBoolean())).thenReturn(null); 551 552 // Set screen to off 553 mWifiConnectivityManager.handleScreenStateChanged(false); 554 555 // Set WiFi to disconnected state to trigger PNO scan 556 mWifiConnectivityManager.handleConnectionStateChanged( 557 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 558 559 // Now fire the watchdog alarm and verify the metrics were incremented. 560 mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG); 561 mLooper.dispatchAll(); 562 563 verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood(); 564 verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad(); 565 } 566 567 /** 568 * Verify that scan interval for screen on and wifi disconnected scenario 569 * is in the exponential backoff fashion. 570 * 571 * Expected behavior: WifiConnectivityManager doubles periodic 572 * scan interval. 573 */ 574 @Test 575 public void checkPeriodicScanIntervalWhenDisconnected() { 576 long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; 577 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 578 579 // Set screen to ON 580 mWifiConnectivityManager.handleScreenStateChanged(true); 581 582 // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered 583 // by screen state change can settle 584 currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS; 585 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 586 587 // Set WiFi to disconnected state to trigger periodic scan 588 mWifiConnectivityManager.handleConnectionStateChanged( 589 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 590 591 // Get the first periodic scan interval 592 long firstIntervalMs = mAlarmManager 593 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 594 - currentTimeStamp; 595 assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS); 596 597 currentTimeStamp += firstIntervalMs; 598 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 599 600 // Now fire the first periodic scan alarm timer 601 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 602 mLooper.dispatchAll(); 603 604 // Get the second periodic scan interval 605 long secondIntervalMs = mAlarmManager 606 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 607 - currentTimeStamp; 608 609 // Verify the intervals are exponential back off 610 assertEquals(firstIntervalMs * 2, secondIntervalMs); 611 612 currentTimeStamp += secondIntervalMs; 613 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 614 615 // Make sure we eventually stay at the maximum scan interval. 616 long intervalMs = 0; 617 for (int i = 0; i < 5; i++) { 618 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 619 mLooper.dispatchAll(); 620 intervalMs = mAlarmManager 621 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 622 - currentTimeStamp; 623 currentTimeStamp += intervalMs; 624 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 625 } 626 627 assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS); 628 } 629 630 /** 631 * Verify that scan interval for screen on and wifi connected scenario 632 * is in the exponential backoff fashion. 633 * 634 * Expected behavior: WifiConnectivityManager doubles periodic 635 * scan interval. 636 */ 637 @Test 638 public void checkPeriodicScanIntervalWhenConnected() { 639 long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; 640 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 641 642 // Set screen to ON 643 mWifiConnectivityManager.handleScreenStateChanged(true); 644 645 // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered 646 // by screen state change can settle 647 currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS; 648 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 649 650 // Set WiFi to connected state to trigger periodic scan 651 mWifiConnectivityManager.handleConnectionStateChanged( 652 WifiConnectivityManager.WIFI_STATE_CONNECTED); 653 654 // Get the first periodic scan interval 655 long firstIntervalMs = mAlarmManager 656 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 657 - currentTimeStamp; 658 assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS); 659 660 currentTimeStamp += firstIntervalMs; 661 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 662 663 // Now fire the first periodic scan alarm timer 664 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 665 mLooper.dispatchAll(); 666 667 // Get the second periodic scan interval 668 long secondIntervalMs = mAlarmManager 669 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 670 - currentTimeStamp; 671 672 // Verify the intervals are exponential back off 673 assertEquals(firstIntervalMs * 2, secondIntervalMs); 674 675 currentTimeStamp += secondIntervalMs; 676 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 677 678 // Make sure we eventually stay at the maximum scan interval. 679 long intervalMs = 0; 680 for (int i = 0; i < 5; i++) { 681 mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 682 mLooper.dispatchAll(); 683 intervalMs = mAlarmManager 684 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG) 685 - currentTimeStamp; 686 currentTimeStamp += intervalMs; 687 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 688 } 689 690 assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS); 691 } 692 693 /** 694 * When screen on trigger two connection state change events back to back to 695 * verify that the minium scan interval is enforced. 696 * 697 * Expected behavior: WifiConnectivityManager start the second periodic single 698 * scan PERIODIC_SCAN_INTERVAL_MS after the first one. 699 */ 700 @Test 701 public void checkMinimumPeriodicScanIntervalWhenScreenOn() { 702 long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; 703 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 704 705 // Set screen to ON 706 mWifiConnectivityManager.handleScreenStateChanged(true); 707 708 // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered 709 // by screen state change can settle 710 currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS; 711 long firstScanTimeStamp = currentTimeStamp; 712 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 713 714 // Set WiFi to connected state to trigger the periodic scan 715 mWifiConnectivityManager.handleConnectionStateChanged( 716 WifiConnectivityManager.WIFI_STATE_CONNECTED); 717 718 // Set the second scan attempt time stamp. 719 currentTimeStamp += 2000; 720 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 721 722 // Set WiFi to disconnected state to trigger another periodic scan 723 mWifiConnectivityManager.handleConnectionStateChanged( 724 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 725 726 // Get the second periodic scan actual time stamp 727 long secondScanTimeStamp = mAlarmManager 728 .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG); 729 730 // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after the 731 // very first scan. 732 assertEquals(secondScanTimeStamp, firstScanTimeStamp 733 + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS); 734 735 } 736 737 /** 738 * When screen on trigger a connection state change event and a forced connectivity 739 * scan event back to back to verify that the minimum scan interval is not applied 740 * in this scenario. 741 * 742 * Expected behavior: WifiConnectivityManager starts the second periodic single 743 * scan immediately. 744 */ 745 @Test 746 public void checkMinimumPeriodicScanIntervalNotEnforced() { 747 long currentTimeStamp = CURRENT_SYSTEM_TIME_MS; 748 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 749 750 // Set screen to ON 751 mWifiConnectivityManager.handleScreenStateChanged(true); 752 753 // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered 754 // by screen state change can settle 755 currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS; 756 long firstScanTimeStamp = currentTimeStamp; 757 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 758 759 // Set WiFi to connected state to trigger the periodic scan 760 mWifiConnectivityManager.handleConnectionStateChanged( 761 WifiConnectivityManager.WIFI_STATE_CONNECTED); 762 763 // Set the second scan attempt time stamp 764 currentTimeStamp += 2000; 765 when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp); 766 767 // Force a connectivity scan 768 mWifiConnectivityManager.forceConnectivityScan(); 769 770 // Get the second periodic scan actual time stamp. Note, this scan is not 771 // started from the AlarmManager. 772 long secondScanTimeStamp = mWifiConnectivityManager.getLastPeriodicSingleScanTimeStamp(); 773 774 // Verify that the second scan is fired immediately 775 assertEquals(secondScanTimeStamp, currentTimeStamp); 776 } 777 778 /** 779 * Verify that we perform full band scan when the currently connected network's tx/rx success 780 * rate is low. 781 * 782 * Expected behavior: WifiConnectivityManager does full band scan. 783 */ 784 @Test 785 public void checkSingleScanSettingsWhenConnectedWithLowDataRate() { 786 mWifiInfo.txSuccessRate = 0; 787 mWifiInfo.rxSuccessRate = 0; 788 789 final HashSet<Integer> channelList = new HashSet<>(); 790 channelList.add(1); 791 channelList.add(2); 792 channelList.add(3); 793 794 when(mWifiStateMachine.getCurrentWifiConfiguration()) 795 .thenReturn(new WifiConfiguration()); 796 when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyInt())) 797 .thenReturn(channelList); 798 799 doAnswer(new AnswerWithArguments() { 800 public void answer(ScanSettings settings, ScanListener listener, 801 WorkSource workSource) throws Exception { 802 assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS); 803 assertNull(settings.channels); 804 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 805 806 // Set screen to ON 807 mWifiConnectivityManager.handleScreenStateChanged(true); 808 809 // Set WiFi to connected state to trigger periodic scan 810 mWifiConnectivityManager.handleConnectionStateChanged( 811 WifiConnectivityManager.WIFI_STATE_CONNECTED); 812 813 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 814 } 815 816 /** 817 * Verify that we perform partial scan when the currently connected network's tx/rx success 818 * rate is high and when the currently connected network is present in scan 819 * cache in WifiConfigManager. 820 * 821 * Expected behavior: WifiConnectivityManager does full band scan. 822 */ 823 @Test 824 public void checkSingleScanSettingsWhenConnectedWithHighDataRate() { 825 mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2; 826 mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2; 827 828 final HashSet<Integer> channelList = new HashSet<>(); 829 channelList.add(1); 830 channelList.add(2); 831 channelList.add(3); 832 833 when(mWifiStateMachine.getCurrentWifiConfiguration()) 834 .thenReturn(new WifiConfiguration()); 835 when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyInt())) 836 .thenReturn(channelList); 837 838 doAnswer(new AnswerWithArguments() { 839 public void answer(ScanSettings settings, ScanListener listener, 840 WorkSource workSource) throws Exception { 841 assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED); 842 assertEquals(settings.channels.length, channelList.size()); 843 for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) { 844 assertTrue(channelList.contains(settings.channels[chanIdx].frequency)); 845 } 846 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 847 848 // Set screen to ON 849 mWifiConnectivityManager.handleScreenStateChanged(true); 850 851 // Set WiFi to connected state to trigger periodic scan 852 mWifiConnectivityManager.handleConnectionStateChanged( 853 WifiConnectivityManager.WIFI_STATE_CONNECTED); 854 855 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 856 } 857 858 /** 859 * Verify that we fall back to full band scan when the currently connected network's tx/rx 860 * success rate is high and the currently connected network is not present in scan cache in 861 * WifiConfigManager. This is simulated by returning an empty hashset in |makeChannelList|. 862 * 863 * Expected behavior: WifiConnectivityManager does full band scan. 864 */ 865 @Test 866 public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() { 867 mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2; 868 mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2; 869 870 final HashSet<Integer> channelList = new HashSet<>(); 871 872 when(mWifiStateMachine.getCurrentWifiConfiguration()) 873 .thenReturn(new WifiConfiguration()); 874 when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyInt())) 875 .thenReturn(channelList); 876 877 doAnswer(new AnswerWithArguments() { 878 public void answer(ScanSettings settings, ScanListener listener, 879 WorkSource workSource) throws Exception { 880 assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS); 881 assertNull(settings.channels); 882 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 883 884 // Set screen to ON 885 mWifiConnectivityManager.handleScreenStateChanged(true); 886 887 // Set WiFi to connected state to trigger periodic scan 888 mWifiConnectivityManager.handleConnectionStateChanged( 889 WifiConnectivityManager.WIFI_STATE_CONNECTED); 890 891 verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 892 } 893 894 /** 895 * Verify that we retry connectivity scan up to MAX_SCAN_RESTART_ALLOWED times 896 * when Wifi somehow gets into a bad state and fails to scan. 897 * 898 * Expected behavior: WifiConnectivityManager schedules connectivity scan 899 * MAX_SCAN_RESTART_ALLOWED times. 900 */ 901 @Test 902 public void checkMaximumScanRetry() { 903 // Set screen to ON 904 mWifiConnectivityManager.handleScreenStateChanged(true); 905 906 doAnswer(new AnswerWithArguments() { 907 public void answer(ScanSettings settings, ScanListener listener, 908 WorkSource workSource) throws Exception { 909 listener.onFailure(-1, "ScanFailure"); 910 }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); 911 912 // Set WiFi to disconnected state to trigger the single scan based periodic scan 913 mWifiConnectivityManager.handleConnectionStateChanged( 914 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 915 916 // Fire the alarm timer 2x timers 917 for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) { 918 mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); 919 mLooper.dispatchAll(); 920 } 921 922 // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED 923 // times. Note, WifiScanner.startScan() is invoked MAX_SCAN_RESTART_ALLOWED + 1 times. 924 // The very first scan is the initial one, and the other MAX_SCAN_RESTART_ALLOWED 925 // are the retrial ones. 926 verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 1)).startScan( 927 anyObject(), anyObject(), anyObject()); 928 } 929 930 /** 931 * Listen to scan results not requested by WifiConnectivityManager and 932 * act on them. 933 * 934 * Expected behavior: WifiConnectivityManager calls 935 * WifiStateMachine.startConnectToNetwork() with the 936 * expected candidate network ID and BSSID. 937 */ 938 @Test 939 public void listenToAllSingleScanResults() { 940 ScanSettings settings = new ScanSettings(); 941 ScanListener scanListener = mock(ScanListener.class); 942 943 // Request a single scan outside of WifiConnectivityManager. 944 mWifiScanner.startScan(settings, scanListener, WIFI_WORK_SOURCE); 945 946 // Verify that WCM receives the scan results and initiates a connection 947 // to the network. 948 verify(mWifiStateMachine).startConnectToNetwork( 949 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 950 } 951} 952