SupplicantPnoScannerTest.java revision 1bf983a4211f547593a60523e43112ecdb5c8997
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.scanner; 18 19import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; 20import static com.android.server.wifi.ScanTestUtil.assertScanDataEquals; 21 22import static org.junit.Assert.*; 23import static org.mockito.Mockito.*; 24 25import android.content.Context; 26import android.net.wifi.WifiConfiguration; 27import android.net.wifi.WifiScanner; 28import android.os.SystemClock; 29import android.test.suitebuilder.annotation.SmallTest; 30 31import com.android.internal.R; 32import com.android.server.wifi.Clock; 33import com.android.server.wifi.MockAlarmManager; 34import com.android.server.wifi.MockLooper; 35import com.android.server.wifi.MockResources; 36import com.android.server.wifi.MockWifiMonitor; 37import com.android.server.wifi.ScanResults; 38import com.android.server.wifi.WifiMonitor; 39import com.android.server.wifi.WifiNative; 40import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; 41 42import org.junit.Before; 43import org.junit.Test; 44import org.mockito.InOrder; 45import org.mockito.Mock; 46import org.mockito.MockitoAnnotations; 47 48import java.util.Arrays; 49import java.util.HashSet; 50import java.util.Set; 51 52 53/** 54 * Unit tests for {@link com.android.server.wifi.scanner.SupplicantWifiScannerImpl.setPnoList}. 55 */ 56@SmallTest 57public class SupplicantPnoScannerTest { 58 59 @Mock Context mContext; 60 MockAlarmManager mAlarmManager; 61 MockWifiMonitor mWifiMonitor; 62 MockLooper mLooper; 63 @Mock WifiNative mWifiNative; 64 MockResources mResources; 65 @Mock Clock mClock; 66 SupplicantWifiScannerImpl mScanner; 67 68 @Before 69 public void setup() throws Exception { 70 MockitoAnnotations.initMocks(this); 71 72 mLooper = new MockLooper(); 73 mAlarmManager = new MockAlarmManager(); 74 mWifiMonitor = new MockWifiMonitor(); 75 mResources = new MockResources(); 76 77 when(mWifiNative.getInterfaceName()).thenReturn("a_test_interface_name"); 78 when(mContext.getSystemService(Context.ALARM_SERVICE)) 79 .thenReturn(mAlarmManager.getAlarmManager()); 80 when(mContext.getResources()).thenReturn(mResources); 81 when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime()); 82 } 83 84 /** 85 * Verify that the HW disconnected PNO scan triggers a supplicant PNO scan and invokes the 86 * OnPnoNetworkFound callback when the scan results are received. 87 */ 88 @Test 89 public void startHwDisconnectedPnoScan() { 90 createScannerWithHwPnoScanSupport(); 91 92 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 93 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 94 ScanResults scanResults = createDummyScanResults(); 95 96 InOrder order = inOrder(pnoEventHandler, mWifiNative); 97 // Start PNO scan 98 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 99 expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults); 100 verifyNoMoreInteractions(pnoEventHandler); 101 } 102 103 /** 104 * Verify that we pause & resume HW PNO scan when a single scan is scheduled and invokes the 105 * OnPnoNetworkFound callback when the scan results are received. 106 */ 107 @Test 108 public void pauseResumeHwDisconnectedPnoScanForSingleScan() { 109 createScannerWithHwPnoScanSupport(); 110 111 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 112 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 113 WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); 114 WifiNative.ScanSettings settings = createDummyScanSettings(); 115 ScanResults scanResults = createDummyScanResults(); 116 117 InOrder order = inOrder(eventHandler, mWifiNative); 118 // Start PNO scan 119 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 120 // Start single scan 121 assertTrue(mScanner.startSingleScan(settings, eventHandler)); 122 // Verify that the PNO scan was paused and single scan runs successfully 123 expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler, 124 expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(), 125 scanResults); 126 verifyNoMoreInteractions(eventHandler); 127 128 order = inOrder(pnoEventHandler, mWifiNative); 129 // Resume PNO scan after the single scan results are received and PNO monitor debounce 130 // alarm fires. 131 assertTrue("dispatch pno monitor alarm", 132 mAlarmManager.dispatch( 133 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 134 assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll()); 135 // Now verify that PNO scan is resumed successfully 136 expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults); 137 verifyNoMoreInteractions(pnoEventHandler); 138 } 139 140 /** 141 * Verify that the SW disconnected PNO scan triggers a background scan and invokes the 142 * background scan callbacks when scan results are received. 143 */ 144 @Test 145 public void startSwDisconnectedPnoScan() { 146 createScannerWithSwPnoScanSupport(); 147 doSuccessfulSwPnoScanTest(false); 148 } 149 150 /** 151 * Verify that the HW connected PNO scan triggers a background scan and invokes the 152 * background scan callbacks when scan results are received. 153 */ 154 @Test 155 public void startHwConnectedPnoScan() { 156 createScannerWithHwPnoScanSupport(); 157 doSuccessfulSwPnoScanTest(true); 158 } 159 160 /** 161 * Verify that the SW connected PNO scan triggers a background scan and invokes the 162 * background scan callbacks when scan results are received. 163 */ 164 @Test 165 public void startSwConnectedPnoScan() { 166 createScannerWithSwPnoScanSupport(); 167 doSuccessfulSwPnoScanTest(true); 168 } 169 170 /** 171 * Verify that the HW PNO delayed failure cleans up the scan settings cleanly. 172 * 1. Start Hw PNO. 173 * 2. Start Single Scan which should pause PNO scan. 174 * 3. Fail the PNO scan resume and verify that the OnPnoScanFailed callback is invoked. 175 * 4. Now restart a new PNO scan to ensure that the failure was cleanly handled. 176 */ 177 @Test 178 public void delayedHwDisconnectedPnoScanFailure() { 179 createScannerWithHwPnoScanSupport(); 180 181 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 182 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 183 WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); 184 WifiNative.ScanSettings settings = createDummyScanSettings(); 185 ScanResults scanResults = createDummyScanResults(); 186 187 InOrder order = inOrder(eventHandler, mWifiNative); 188 // Start PNO scan 189 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 190 // Start single scan 191 assertTrue(mScanner.startSingleScan(settings, eventHandler)); 192 // Verify that the PNO scan was paused and single scan runs successfully 193 expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler, 194 expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(), 195 scanResults); 196 verifyNoMoreInteractions(eventHandler); 197 198 // Fail the PNO resume and check that the OnPnoScanFailed callback is invoked. 199 order = inOrder(pnoEventHandler, mWifiNative); 200 when(mWifiNative.setPnoScan(true)).thenReturn(false); 201 assertTrue("dispatch pno monitor alarm", 202 mAlarmManager.dispatch( 203 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 204 assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll()); 205 order.verify(pnoEventHandler).onPnoScanFailed(); 206 verifyNoMoreInteractions(pnoEventHandler); 207 208 // Add a new PNO scan request 209 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 210 assertTrue("dispatch pno monitor alarm", 211 mAlarmManager.dispatch( 212 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 213 assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll()); 214 expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults); 215 verifyNoMoreInteractions(pnoEventHandler); 216 } 217 218 /** 219 * Verify that the HW PNO scan stop failure still resets the PNO scan state. 220 * 1. Start Hw PNO. 221 * 2. Stop Hw PNO scan which raises a stop command to WifiNative which is failed. 222 * 3. Now restart a new PNO scan to ensure that the failure was cleanly handled. 223 */ 224 @Test 225 public void ignoreHwDisconnectedPnoScanStopFailure() { 226 createScannerWithHwPnoScanSupport(); 227 228 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 229 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 230 231 // Start PNO scan 232 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 233 234 // Fail the PNO stop. 235 when(mWifiNative.setPnoScan(false)).thenReturn(false); 236 assertTrue(mScanner.resetHwPnoList()); 237 assertTrue("dispatch pno monitor alarm", 238 mAlarmManager.dispatch( 239 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 240 mLooper.dispatchAll(); 241 verify(mWifiNative).setPnoScan(false); 242 243 // Add a new PNO scan request and ensure it runs successfully. 244 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 245 assertTrue("dispatch pno monitor alarm", 246 mAlarmManager.dispatch( 247 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 248 mLooper.dispatchAll(); 249 InOrder order = inOrder(pnoEventHandler, mWifiNative); 250 ScanResults scanResults = createDummyScanResults(); 251 expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults); 252 verifyNoMoreInteractions(pnoEventHandler); 253 } 254 255 /** 256 * Verify that the HW PNO scan is forcefully stopped (bypass debounce logic) and restarted when 257 * settings change. 258 * 1. Start Hw PNO. 259 * 2. Stop Hw PNO . 260 * 3. Now restart a new PNO scan with different settings. 261 * 4. Ensure that the stop was issued before we start again. 262 */ 263 @Test 264 public void forceRestartHwDisconnectedPnoScanWhenSettingsChange() { 265 createScannerWithHwPnoScanSupport(); 266 267 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 268 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 269 InOrder order = inOrder(pnoEventHandler, mWifiNative); 270 271 // Start PNO scan 272 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 273 expectHwDisconnectedPnoScanStart(order, pnoSettings); 274 275 // Stop PNO now. This should trigger the debounce timer and not stop PNO. 276 assertTrue(mScanner.resetHwPnoList()); 277 assertTrue(mAlarmManager.isPending( 278 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 279 order.verify(mWifiNative, never()).setPnoScan(false); 280 281 // Now restart PNO scan with an extra network in settings. 282 pnoSettings.networkList = 283 Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1); 284 pnoSettings.networkList[pnoSettings.networkList.length - 1] = 285 createDummyPnoNetwork("ssid_pno_new", 6, 6); 286 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 287 288 // This should bypass the debounce timer and stop PNO scan immediately and then start 289 // a new debounce timer for the start. 290 order.verify(mWifiNative).setPnoScan(false); 291 292 // Trigger the debounce timer and ensure we start PNO scan again. 293 mAlarmManager.dispatch(SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG); 294 mLooper.dispatchAll(); 295 order.verify(mWifiNative).setPnoScan(true); 296 } 297 298 /** 299 * Verify that the HW PNO scan is not forcefully stopped (bypass debounce logic) when 300 * settings don't change. 301 * 1. Start Hw PNO. 302 * 2. Stop Hw PNO . 303 * 3. Now restart a new PNO scan with same settings. 304 * 4. Ensure that the stop was never issued. 305 */ 306 @Test 307 public void noForceRestartHwDisconnectedPnoScanWhenNoSettingsChange() { 308 createScannerWithHwPnoScanSupport(); 309 310 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 311 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); 312 InOrder order = inOrder(pnoEventHandler, mWifiNative); 313 314 // Start PNO scan 315 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 316 expectHwDisconnectedPnoScanStart(order, pnoSettings); 317 318 // Stop PNO now. This should trigger the debounce timer and not stop PNO. 319 assertTrue(mScanner.resetHwPnoList()); 320 assertTrue(mAlarmManager.isPending( 321 SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); 322 order.verify(mWifiNative, never()).setPnoScan(false); 323 324 // Now restart PNO scan with the same settings. 325 startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); 326 327 // Trigger the debounce timer and ensure that we neither stop/start. 328 mLooper.dispatchAll(); 329 order.verify(mWifiNative, never()).setPnoScan(anyBoolean()); 330 } 331 332 private void doSuccessfulSwPnoScanTest(boolean isConnectedPno) { 333 WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); 334 WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(isConnectedPno); 335 WifiNative.ScanEventHandler scanEventHandler = mock(WifiNative.ScanEventHandler.class); 336 WifiNative.ScanSettings scanSettings = createDummyScanSettings(); 337 ScanResults scanResults = createDummyScanResults(); 338 339 InOrder order = inOrder(scanEventHandler, mWifiNative); 340 341 // Start PNO scan 342 startSuccessfulPnoScan(scanSettings, pnoSettings, scanEventHandler, pnoEventHandler); 343 344 expectSuccessfulSwPnoScan(order, scanEventHandler, scanResults); 345 346 verifyNoMoreInteractions(pnoEventHandler); 347 } 348 349 private void createScannerWithHwPnoScanSupport() { 350 mResources.setBoolean(R.bool.config_wifi_background_scan_support, true); 351 mScanner = 352 new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock); 353 } 354 355 private void createScannerWithSwPnoScanSupport() { 356 mResources.setBoolean(R.bool.config_wifi_background_scan_support, false); 357 mScanner = 358 new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock); 359 } 360 361 private WifiNative.PnoNetwork createDummyPnoNetwork(String ssid, int networkId, int priority) { 362 WifiNative.PnoNetwork pnoNetwork = new WifiNative.PnoNetwork(); 363 pnoNetwork.ssid = ssid; 364 pnoNetwork.networkId = networkId; 365 pnoNetwork.priority = priority; 366 return pnoNetwork; 367 } 368 369 private WifiNative.PnoSettings createDummyPnoSettings(boolean isConnected) { 370 WifiNative.PnoSettings pnoSettings = new WifiNative.PnoSettings(); 371 pnoSettings.isConnected = isConnected; 372 pnoSettings.networkList = new WifiNative.PnoNetwork[2]; 373 pnoSettings.networkList[0] = createDummyPnoNetwork("ssid_pno_1", 1, 1); 374 pnoSettings.networkList[1] = createDummyPnoNetwork("ssid_pno_2", 2, 2); 375 return pnoSettings; 376 } 377 378 private WifiNative.ScanSettings createDummyScanSettings() { 379 WifiNative.ScanSettings settings = new NativeScanSettingsBuilder() 380 .withBasePeriod(10000) 381 .withMaxApPerScan(10) 382 .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 383 WifiScanner.WIFI_BAND_24_GHZ) 384 .build(); 385 return settings; 386 } 387 388 private ScanResults createDummyScanResults() { 389 return ScanResults.create(0, 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450); 390 } 391 392 private void startSuccessfulPnoScan(WifiNative.ScanSettings scanSettings, 393 WifiNative.PnoSettings pnoSettings, WifiNative.ScanEventHandler scanEventHandler, 394 WifiNative.PnoEventHandler pnoEventHandler) { 395 reset(mWifiNative); 396 when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true); 397 when(mWifiNative.enableNetworkWithoutConnect(anyInt())).thenReturn(true); 398 // Scans succeed 399 when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true); 400 when(mWifiNative.setPnoScan(anyBoolean())).thenReturn(true); 401 402 if (mScanner.isHwPnoSupported(pnoSettings.isConnected)) { 403 // This should happen only for HW PNO scan 404 assertTrue(mScanner.setHwPnoList(pnoSettings, pnoEventHandler)); 405 } else { 406 // This should happen only for SW PNO scan 407 assertTrue(mScanner.startBatchedScan(scanSettings, scanEventHandler)); 408 409 } 410 } 411 412 private Set<Integer> expectedBandScanFreqs(int band) { 413 ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection(); 414 collection.addBand(band); 415 return collection.getSupplicantScanFreqs(); 416 } 417 418 /** 419 * Verify that the PNO scan was successfully started. 420 */ 421 private void expectHwDisconnectedPnoScanStart(InOrder order, 422 WifiNative.PnoSettings pnoSettings) { 423 for (int i = 0; i < pnoSettings.networkList.length; i++) { 424 WifiNative.PnoNetwork network = pnoSettings.networkList[i]; 425 order.verify(mWifiNative).setNetworkVariable(network.networkId, 426 WifiConfiguration.priorityVarName, Integer.toString(network.priority)); 427 order.verify(mWifiNative).enableNetworkWithoutConnect(network.networkId); 428 } 429 // Verify HW PNO scan started 430 order.verify(mWifiNative).setPnoScan(true); 431 } 432 433 /** 434 * 435 * 1. Verify that the PNO scan was successfully started. 436 * 2. Send scan results and ensure that the |onPnoNetworkFound| callback was called. 437 */ 438 private void expectSuccessfulHwDisconnectedPnoScan(InOrder order, 439 WifiNative.PnoSettings pnoSettings, WifiNative.PnoEventHandler eventHandler, 440 ScanResults scanResults) { 441 expectHwDisconnectedPnoScanStart(order, pnoSettings); 442 443 // Setup scan results 444 when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); 445 446 // Notify scan has finished 447 mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT); 448 assertEquals("dispatch message after results event", 1, mLooper.dispatchAll()); 449 450 order.verify(eventHandler).onPnoNetworkFound(scanResults.getRawScanResults()); 451 } 452 453 /** 454 * Verify that the single scan results were delivered and that the PNO scan was paused and 455 * resumed either side of it. 456 */ 457 private void expectSuccessfulSingleScanWithHwPnoEnabled(InOrder order, 458 WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScanFreqs, 459 Set<Integer> expectedHiddenNetIds, ScanResults scanResults) { 460 // Pause PNO scan first 461 order.verify(mWifiNative).setPnoScan(false); 462 463 order.verify(mWifiNative).scan(eq(expectedScanFreqs), eq(expectedHiddenNetIds)); 464 465 when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); 466 467 // Notify scan has finished 468 mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT); 469 assertEquals("dispatch message after results event", 1, mLooper.dispatchAll()); 470 471 order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 472 assertScanDataEquals(scanResults.getScanData(), mScanner.getLatestSingleScanResults()); 473 } 474 475 /** 476 * Verify that the SW PNO scan was successfully started. This could either be disconnected 477 * or connected PNO. 478 * This is basically ensuring that the background scan runs successfully and returns the 479 * expected result. 480 */ 481 private void expectSuccessfulSwPnoScan(InOrder order, 482 WifiNative.ScanEventHandler eventHandler, ScanResults scanResults) { 483 484 // Verify scan started 485 order.verify(mWifiNative).scan(any(Set.class), any(Set.class)); 486 487 // Make sure that HW PNO scan was not started 488 verify(mWifiNative, never()).setPnoScan(anyBoolean()); 489 490 // Setup scan results 491 when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); 492 493 // Notify scan has finished 494 mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT); 495 assertEquals("dispatch message after results event", 1, mLooper.dispatchAll()); 496 497 // Verify background scan results delivered 498 order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 499 WifiScanner.ScanData[] scanData = mScanner.getLatestBatchedScanResults(true); 500 WifiScanner.ScanData lastScanData = scanData[scanData.length -1]; 501 assertScanDataEquals(scanResults.getScanData(), lastScanData); 502 } 503} 504