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