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