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