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 package com.android.settingslib.wifi; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.mockito.Mockito.any; 25 import static org.mockito.Mockito.anyInt; 26 import static org.mockito.Mockito.doAnswer; 27 import static org.mockito.Mockito.doNothing; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.times; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.net.ConnectivityManager; 39 import android.net.Network; 40 import android.net.NetworkInfo; 41 import android.net.NetworkKey; 42 import android.net.NetworkScoreManager; 43 import android.net.RssiCurve; 44 import android.net.ScoredNetwork; 45 import android.net.WifiKey; 46 import android.net.wifi.ScanResult; 47 import android.net.wifi.WifiConfiguration; 48 import android.net.wifi.WifiInfo; 49 import android.net.wifi.WifiManager; 50 import android.net.wifi.WifiNetworkScoreCache; 51 import android.net.wifi.WifiSsid; 52 import android.net.wifi.hotspot2.OsuProvider; 53 import android.net.wifi.hotspot2.PasspointConfiguration; 54 import android.os.Bundle; 55 import android.os.Handler; 56 import android.os.HandlerThread; 57 import android.os.SystemClock; 58 import android.provider.Settings; 59 import android.util.ArraySet; 60 import android.util.Pair; 61 62 import androidx.test.InstrumentationRegistry; 63 import androidx.test.filters.SmallTest; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import com.android.settingslib.utils.ThreadUtils; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 import org.mockito.ArgumentCaptor; 73 import org.mockito.Captor; 74 import org.mockito.Matchers; 75 import org.mockito.Mock; 76 import org.mockito.MockitoAnnotations; 77 import org.mockito.invocation.InvocationOnMock; 78 import org.mockito.stubbing.Answer; 79 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.HashMap; 83 import java.util.List; 84 import java.util.Map; 85 import java.util.Set; 86 import java.util.concurrent.CountDownLatch; 87 import java.util.concurrent.TimeUnit; 88 import java.util.concurrent.atomic.AtomicBoolean; 89 90 // TODO(sghuman): Change these to robolectric tests b/35766684. 91 @SmallTest 92 @RunWith(AndroidJUnit4.class) 93 public class WifiTrackerTest { 94 95 private static final String TAG = "WifiTrackerTest"; 96 private static final int LATCH_TIMEOUT = 4000; 97 98 private static final String SSID_1 = "ssid1"; 99 private static final String BSSID_1 = "00:00:00:00:00:00"; 100 private static final NetworkKey NETWORK_KEY_1 = 101 new NetworkKey(new WifiKey('"' + SSID_1 + '"', BSSID_1)); 102 private static final int RSSI_1 = -30; 103 private static final byte SCORE_1 = 10; 104 private static final int BADGE_1 = AccessPoint.Speed.MODERATE; 105 private static final String FQDN_1 = "fqdn1"; 106 private static final String PROVIDER_FRIENDLY_NAME_1 = "providerFriendlyName1"; 107 108 private static final String SSID_2 = "ssid2"; 109 private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA"; 110 private static final NetworkKey NETWORK_KEY_2 = 111 new NetworkKey(new WifiKey('"' + SSID_2 + '"', BSSID_2)); 112 private static final int RSSI_2 = -30; 113 private static final byte SCORE_2 = 15; 114 private static final int BADGE_2 = AccessPoint.Speed.FAST; 115 private static final String FQDN_2 = "fqdn2"; 116 private static final String PROVIDER_FRIENDLY_NAME_2 = "providerFriendlyName2"; 117 118 private static final String SSID_3 = "ssid3"; 119 private static final String BSSID_3 = "CC:00:00:00:00:00"; 120 private static final int RSSI_3 = -40; 121 122 // TODO(b/65594609): Convert mutable Data objects to instance variables / builder pattern 123 private static final int NETWORK_ID_1 = 123; 124 private static final int CONNECTED_RSSI = -50; 125 private static final WifiInfo CONNECTED_AP_1_INFO = new WifiInfo(); 126 static { WifiSsid.fromUtf8Text(SSID_1)127 CONNECTED_AP_1_INFO.setSSID(WifiSsid.fromUtf8Text(SSID_1)); 128 CONNECTED_AP_1_INFO.setBSSID(BSSID_1); 129 CONNECTED_AP_1_INFO.setNetworkId(NETWORK_ID_1); 130 CONNECTED_AP_1_INFO.setRssi(CONNECTED_RSSI); 131 } 132 private static final WifiConfiguration CONFIGURATION_1 = new WifiConfiguration(); 133 static { 134 CONFIGURATION_1.SSID = SSID_1; 135 CONFIGURATION_1.BSSID = BSSID_1; 136 CONFIGURATION_1.networkId = NETWORK_ID_1; 137 } 138 139 private static final int NETWORK_ID_2 = 2; 140 private static final WifiConfiguration CONFIGURATION_2 = new WifiConfiguration(); 141 static { 142 CONFIGURATION_2.SSID = SSID_2; 143 CONFIGURATION_2.BSSID = BSSID_2; 144 CONFIGURATION_2.networkId = NETWORK_ID_2; 145 } 146 147 @Captor ArgumentCaptor<WifiNetworkScoreCache> mScoreCacheCaptor; 148 @Mock private ConnectivityManager mockConnectivityManager; 149 @Mock private NetworkScoreManager mockNetworkScoreManager; 150 @Mock private RssiCurve mockCurve1; 151 @Mock private RssiCurve mockCurve2; 152 @Mock private RssiCurve mockBadgeCurve1; 153 @Mock private RssiCurve mockBadgeCurve2; 154 @Mock private WifiManager mockWifiManager; 155 @Mock private WifiTracker.WifiListener mockWifiListener; 156 157 private final List<NetworkKey> mRequestedKeys = new ArrayList<>(); 158 159 private Context mContext; 160 private CountDownLatch mAccessPointsChangedLatch; 161 private CountDownLatch mRequestScoresLatch; 162 private Handler mScannerHandler; 163 private HandlerThread mWorkerThread; 164 165 private int mOriginalScoringUiSettingValue; 166 167 @SuppressWarnings("VisibleForTests") 168 @Before setUp()169 public void setUp() { 170 MockitoAnnotations.initMocks(this); 171 172 mContext = InstrumentationRegistry.getTargetContext(); 173 174 mWorkerThread = new HandlerThread("TestHandlerWorkerThread"); 175 mWorkerThread.start(); 176 177 // Make sure the scanner doesn't try to run on the testing thread. 178 HandlerThread scannerThread = new HandlerThread("ScannerWorkerThread"); 179 scannerThread.start(); 180 mScannerHandler = new Handler(scannerThread.getLooper()); 181 182 when(mockWifiManager.isWifiEnabled()).thenReturn(true); 183 when(mockWifiManager.getScanResults()) 184 .thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2())); 185 when(mockWifiManager.getConfiguredNetworks()) 186 .thenReturn(Arrays.asList(CONFIGURATION_1, CONFIGURATION_2)); 187 188 189 when(mockCurve1.lookupScore(RSSI_1)).thenReturn(SCORE_1); 190 when(mockCurve2.lookupScore(RSSI_2)).thenReturn(SCORE_2); 191 192 when(mockBadgeCurve1.lookupScore(RSSI_1)).thenReturn((byte) BADGE_1); 193 when(mockBadgeCurve2.lookupScore(RSSI_2)).thenReturn((byte) BADGE_2); 194 195 doNothing() 196 .when(mockNetworkScoreManager) 197 .registerNetworkScoreCache( 198 anyInt(), 199 mScoreCacheCaptor.capture(), 200 Matchers.anyInt()); 201 202 // Capture requested keys and count down latch if present 203 doAnswer( 204 new Answer<Boolean>() { 205 @Override 206 public Boolean answer(InvocationOnMock input) { 207 if (mRequestScoresLatch != null) { 208 mRequestScoresLatch.countDown(); 209 } 210 NetworkKey[] keys = (NetworkKey[]) input.getArguments()[0]; 211 for (NetworkKey key : keys) { 212 mRequestedKeys.add(key); 213 } 214 return true; 215 } 216 }).when(mockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any()); 217 218 // We use a latch to detect callbacks as Tracker initialization state often invokes 219 // callbacks 220 doAnswer(invocation -> { 221 if (mAccessPointsChangedLatch != null) { 222 mAccessPointsChangedLatch.countDown(); 223 } 224 return null; 225 }).when(mockWifiListener).onAccessPointsChanged(); 226 227 // Turn on Scoring UI features 228 mOriginalScoringUiSettingValue = Settings.Global.getInt( 229 InstrumentationRegistry.getTargetContext().getContentResolver(), 230 Settings.Global.NETWORK_SCORING_UI_ENABLED, 231 0 /* disabled */); 232 Settings.Global.putInt( 233 InstrumentationRegistry.getTargetContext().getContentResolver(), 234 Settings.Global.NETWORK_SCORING_UI_ENABLED, 235 1 /* enabled */); 236 237 } 238 239 @After cleanUp()240 public void cleanUp() { 241 Settings.Global.putInt( 242 InstrumentationRegistry.getTargetContext().getContentResolver(), 243 Settings.Global.NETWORK_SCORING_UI_ENABLED, 244 mOriginalScoringUiSettingValue); 245 } 246 buildScanResult1()247 private static ScanResult buildScanResult1() { 248 return new ScanResult( 249 WifiSsid.fromUtf8Text(SSID_1), 250 BSSID_1, 251 0, // hessid 252 0, //anqpDomainId 253 null, // osuProviders 254 "", // capabilities 255 RSSI_1, 256 0, // frequency 257 SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */); 258 } 259 buildScanResult2()260 private static ScanResult buildScanResult2() { 261 return new ScanResult( 262 WifiSsid.fromUtf8Text(SSID_2), 263 BSSID_2, 264 0, // hessid 265 0, //anqpDomainId 266 null, // osuProviders 267 "", // capabilities 268 RSSI_2, 269 0, // frequency 270 SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */); 271 } 272 buildScanResultWithTimestamp(long timestampMillis)273 private static ScanResult buildScanResultWithTimestamp(long timestampMillis) { 274 return new ScanResult( 275 WifiSsid.fromUtf8Text(SSID_3), 276 BSSID_3, 277 0, // hessid 278 0, //anqpDomainId 279 null, // osuProviders 280 "", // capabilities 281 RSSI_3, 282 0, // frequency 283 timestampMillis * 1000 /* microsecond timestamp */); 284 } 285 buildPasspointConfiguration(String fqdn, String friendlyName)286 private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) { 287 WifiConfiguration config = spy(new WifiConfiguration()); 288 config.FQDN = fqdn; 289 config.providerFriendlyName = friendlyName; 290 when(config.isPasspoint()).thenReturn(true); 291 return config; 292 } 293 294 private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> createPasspointMatchingWifiConfigsWithDuplicates()295 createPasspointMatchingWifiConfigsWithDuplicates() { 296 List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = 297 new ArrayList<>(); 298 Map<Integer, List<ScanResult>> mapping = new HashMap<>(); 299 300 mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, Arrays.asList(buildScanResult1())); 301 302 WifiConfiguration passpointConfig1 = 303 buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1); 304 WifiConfiguration passpointConfig2 = 305 buildPasspointConfiguration(FQDN_2, PROVIDER_FRIENDLY_NAME_2); 306 307 matchingList.add(new Pair(passpointConfig1, mapping)); 308 matchingList.add(new Pair(passpointConfig1, mapping)); 309 matchingList.add(new Pair(passpointConfig2, mapping)); 310 matchingList.add(new Pair(passpointConfig2, mapping)); 311 312 return matchingList; 313 } 314 315 private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> createPasspointMatchingWifiConfigWithScanResults( List<ScanResult> homeList, List<ScanResult> roamingList, String fqdn, String friendlyName)316 createPasspointMatchingWifiConfigWithScanResults( 317 List<ScanResult> homeList, List<ScanResult> roamingList, 318 String fqdn, String friendlyName) { 319 List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = 320 new ArrayList<>(); 321 Map<Integer, List<ScanResult>> mapping = new HashMap<>(); 322 323 if (homeList != null) { 324 mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, homeList); 325 } 326 if (roamingList != null) { 327 mapping.put(WifiManager.PASSPOINT_ROAMING_NETWORK, roamingList); 328 } 329 330 matchingList.add(new Pair(buildPasspointConfiguration(fqdn, friendlyName), 331 mapping)); 332 333 return matchingList; 334 } 335 buildOsuProvider(String friendlyName)336 private static OsuProvider buildOsuProvider(String friendlyName) { 337 Map<String, String> friendlyNames = new HashMap<>(); 338 friendlyNames.put("en", friendlyName); 339 return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null); 340 } 341 createTrackerWithImmediateBroadcastsAndInjectInitialScanResults( Intent .... intents)342 private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults( 343 Intent ... intents) 344 throws InterruptedException { 345 WifiTracker tracker = createMockedWifiTracker(); 346 347 startTracking(tracker); 348 for (Intent intent : intents) { 349 tracker.mReceiver.onReceive(mContext, intent); 350 } 351 352 sendScanResults(tracker); 353 354 return tracker; 355 } 356 createMockedWifiTracker()357 private WifiTracker createMockedWifiTracker() { 358 final WifiTracker wifiTracker = new WifiTracker( 359 mContext, 360 mockWifiListener, 361 mockWifiManager, 362 mockConnectivityManager, 363 mockNetworkScoreManager, 364 new IntentFilter()); // empty filter to ignore system broadcasts 365 wifiTracker.setWorkThread(mWorkerThread); 366 return wifiTracker; 367 } 368 startTracking(WifiTracker tracker)369 private void startTracking(WifiTracker tracker) throws InterruptedException { 370 CountDownLatch latch = new CountDownLatch(1); 371 mScannerHandler.post(() -> { 372 tracker.onStart(); 373 latch.countDown(); 374 }); 375 assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 376 } 377 sendScanResults(WifiTracker tracker)378 private void sendScanResults(WifiTracker tracker) throws InterruptedException { 379 Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 380 tracker.mReceiver.onReceive(mContext, i); 381 } 382 sendFailedScanResults(WifiTracker tracker)383 private void sendFailedScanResults(WifiTracker tracker) throws InterruptedException { 384 Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 385 i.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); 386 tracker.mReceiver.onReceive(mContext, i); 387 } 388 sendUpdatedScores()389 private void sendUpdatedScores() throws InterruptedException { 390 Bundle attr1 = new Bundle(); 391 attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1); 392 ScoredNetwork sc1 = 393 new ScoredNetwork( 394 NETWORK_KEY_1, 395 mockCurve1, 396 false /* meteredHint */, 397 attr1); 398 399 Bundle attr2 = new Bundle(); 400 attr2.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve2); 401 ScoredNetwork sc2 = 402 new ScoredNetwork( 403 NETWORK_KEY_2, 404 mockCurve2, 405 true /* meteredHint */, 406 attr2); 407 408 WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue(); 409 scoreCache.updateScores(Arrays.asList(sc1, sc2)); 410 } 411 createTrackerWithScanResultsAndAccessPoint1Connected()412 private WifiTracker createTrackerWithScanResultsAndAccessPoint1Connected() 413 throws InterruptedException { 414 when(mockWifiManager.getConnectionInfo()).thenReturn(CONNECTED_AP_1_INFO); 415 416 WifiConfiguration configuration = new WifiConfiguration(); 417 configuration.SSID = SSID_1; 418 configuration.BSSID = BSSID_1; 419 configuration.networkId = NETWORK_ID_1; 420 421 NetworkInfo networkInfo = new NetworkInfo( 422 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 423 networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test"); 424 425 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 426 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); 427 WifiTracker tracker = 428 createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(intent); 429 assertThat(tracker.isConnected()).isTrue(); 430 return tracker; 431 } 432 waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)433 private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker) 434 throws InterruptedException { 435 CountDownLatch workerLatch = new CountDownLatch(1); 436 tracker.mWorkHandler.post(() -> workerLatch.countDown()); 437 assertTrue("Latch timed out while waiting for WorkerHandler", 438 workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 439 } 440 switchToNetwork2(WifiTracker tracker)441 private void switchToNetwork2(WifiTracker tracker) throws InterruptedException { 442 NetworkInfo networkInfo = new NetworkInfo( 443 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 444 networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "connecting", "test"); 445 446 WifiInfo info = new WifiInfo(); 447 info.setSSID(WifiSsid.fromUtf8Text(SSID_2)); 448 info.setBSSID(BSSID_2); 449 info.setRssi(CONNECTED_RSSI); 450 info.setNetworkId(NETWORK_ID_2); 451 when(mockWifiManager.getConnectionInfo()).thenReturn(info); 452 453 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 454 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); 455 tracker.mReceiver.onReceive(mContext, intent); 456 } 457 458 @Test startAndStopTrackingShouldRegisterAndUnregisterScoreCache()459 public void startAndStopTrackingShouldRegisterAndUnregisterScoreCache() 460 throws InterruptedException { 461 WifiTracker tracker = createMockedWifiTracker(); 462 463 // Test register 464 startTracking(tracker); 465 verify(mockNetworkScoreManager) 466 .registerNetworkScoreCache( 467 Matchers.anyInt(), 468 mScoreCacheCaptor.capture(), 469 Matchers.anyInt()); 470 471 WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue(); 472 473 CountDownLatch latch = new CountDownLatch(1); 474 doAnswer( 475 (invocation) -> { 476 latch.countDown(); 477 return null; 478 }).when(mockNetworkScoreManager) 479 .unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache); 480 481 // Test unregister 482 tracker.onStop(); 483 484 assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 485 verify(mockNetworkScoreManager) 486 .unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache); 487 } 488 489 @Test testGetNumSavedNetworks()490 public void testGetNumSavedNetworks() throws InterruptedException { 491 WifiConfiguration validConfig = new WifiConfiguration(); 492 validConfig.SSID = SSID_1; 493 validConfig.BSSID = BSSID_1; 494 495 WifiConfiguration selfAddedNoAssociation = new WifiConfiguration(); 496 selfAddedNoAssociation.ephemeral = true; 497 selfAddedNoAssociation.numAssociation = 0; 498 selfAddedNoAssociation.SSID = SSID_2; 499 selfAddedNoAssociation.BSSID = BSSID_2; 500 501 when(mockWifiManager.getConfiguredNetworks()) 502 .thenReturn(Arrays.asList(validConfig, selfAddedNoAssociation)); 503 504 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 505 506 assertEquals(1, tracker.getNumSavedNetworks()); 507 } 508 509 @Test startTrackingShouldSetConnectedAccessPointAsActive()510 public void startTrackingShouldSetConnectedAccessPointAsActive() throws InterruptedException { 511 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 512 513 List<AccessPoint> aps = tracker.getAccessPoints(); 514 515 assertThat(aps).hasSize(2); 516 assertThat(aps.get(0).isActive()).isTrue(); 517 } 518 519 @Test startTrackingAfterStopTracking_shouldRequestNewScores()520 public void startTrackingAfterStopTracking_shouldRequestNewScores() 521 throws InterruptedException { 522 // Start the tracker and inject the initial scan results and then stop tracking 523 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 524 525 tracker.onStop(); 526 mRequestedKeys.clear(); 527 528 mRequestScoresLatch = new CountDownLatch(1); 529 startTracking(tracker); 530 assertTrue("Latch timed out", 531 mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 532 533 assertTrue(mRequestedKeys.contains(NETWORK_KEY_1)); 534 assertTrue(mRequestedKeys.contains(NETWORK_KEY_2)); 535 } 536 537 @Test stopTracking_shouldNotClearExistingScores()538 public void stopTracking_shouldNotClearExistingScores() 539 throws InterruptedException { 540 // Start the tracker and inject the initial scan results and then stop tracking 541 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 542 updateScoresAndWaitForCacheListenerToProcess(tracker); 543 tracker.onStop(); 544 545 assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull(); 546 } 547 548 @Test scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged()549 public void scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged() 550 throws InterruptedException { 551 WifiTracker tracker = createMockedWifiTracker(); 552 startTracking(tracker); 553 sendScanResults(tracker); 554 555 updateScoresAndWaitForCacheListenerToProcess(tracker); 556 } 557 updateScoresAndWaitForCacheListenerToProcess(WifiTracker tracker)558 private void updateScoresAndWaitForCacheListenerToProcess(WifiTracker tracker) 559 throws InterruptedException { 560 // Scores are updated via the cache listener hence we need to wait for the work handler 561 // to finish before proceeding. 562 sendUpdatedScores(); 563 564 // Ensure the work handler has processed the scores inside the cache listener of WifiTracker 565 waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker); 566 } 567 568 @Test scoreCacheUpdateScoresShouldChangeSortOrder()569 public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException { 570 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 571 List<AccessPoint> aps = tracker.getAccessPoints(); 572 assertTrue(aps.size() == 2); 573 assertEquals(aps.get(0).getSsidStr(), SSID_1); 574 assertEquals(aps.get(1).getSsidStr(), SSID_2); 575 576 updateScoresAndWaitForCacheListenerToProcess(tracker); 577 578 aps = tracker.getAccessPoints(); 579 assertTrue(aps.size() == 2); 580 assertEquals(aps.get(0).getSsidStr(), SSID_2); 581 assertEquals(aps.get(1).getSsidStr(), SSID_1); 582 } 583 584 @Test scoreCacheUpdateScoresShouldNotChangeSortOrderWhenSortingDisabled()585 public void scoreCacheUpdateScoresShouldNotChangeSortOrderWhenSortingDisabled() 586 throws InterruptedException { 587 Settings.Global.putInt( 588 InstrumentationRegistry.getTargetContext().getContentResolver(), 589 Settings.Global.NETWORK_SCORING_UI_ENABLED, 590 0 /* disabled */); 591 592 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 593 List<AccessPoint> aps = tracker.getAccessPoints(); 594 assertTrue(aps.size() == 2); 595 assertEquals(aps.get(0).getSsidStr(), SSID_1); 596 assertEquals(aps.get(1).getSsidStr(), SSID_2); 597 598 updateScoresAndWaitForCacheListenerToProcess(tracker); 599 600 aps = tracker.getAccessPoints(); 601 assertTrue(aps.size() == 2); 602 assertEquals(aps.get(0).getSsidStr(), SSID_1); 603 assertEquals(aps.get(1).getSsidStr(), SSID_2); 604 } 605 606 @Test scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint()607 public void scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint() 608 throws InterruptedException { 609 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 610 updateScoresAndWaitForCacheListenerToProcess(tracker); 611 612 List<AccessPoint> aps = tracker.getAccessPoints(); 613 614 for (AccessPoint ap : aps) { 615 if (ap.getSsidStr().equals(SSID_1)) { 616 assertEquals(BADGE_1, ap.getSpeed()); 617 } else if (ap.getSsidStr().equals(SSID_2)) { 618 assertEquals(BADGE_2, ap.getSpeed()); 619 } 620 } 621 } 622 623 @Test scoreCacheUpdateMeteredShouldUpdateAccessPointMetering()624 public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering() 625 throws InterruptedException { 626 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 627 updateScoresAndWaitForCacheListenerToProcess(tracker); 628 629 List<AccessPoint> aps = tracker.getAccessPoints(); 630 631 for (AccessPoint ap : aps) { 632 if (ap.getSsidStr().equals(SSID_1)) { 633 assertFalse(ap.isMetered()); 634 } else if (ap.getSsidStr().equals(SSID_2)) { 635 assertTrue(ap.isMetered()); 636 } 637 } 638 } 639 640 @Test noSpeedsShouldBeInsertedIntoAccessPointWhenScoringUiDisabled()641 public void noSpeedsShouldBeInsertedIntoAccessPointWhenScoringUiDisabled() 642 throws InterruptedException { 643 Settings.Global.putInt( 644 InstrumentationRegistry.getTargetContext().getContentResolver(), 645 Settings.Global.NETWORK_SCORING_UI_ENABLED, 646 0 /* disabled */); 647 648 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 649 updateScoresAndWaitForCacheListenerToProcess(tracker); 650 651 List<AccessPoint> aps = tracker.getAccessPoints(); 652 653 for (AccessPoint ap : aps) { 654 if (ap.getSsidStr().equals(SSID_1)) { 655 assertEquals(AccessPoint.Speed.NONE, ap.getSpeed()); 656 } else if (ap.getSsidStr().equals(SSID_2)) { 657 assertEquals(AccessPoint.Speed.NONE, ap.getSpeed()); 658 } 659 } 660 } 661 662 @Test scoresShouldBeRequestedForNewScanResultOnly()663 public void scoresShouldBeRequestedForNewScanResultOnly() throws InterruptedException { 664 // Scores can be requested together or serially depending on how the scan results are 665 // processed. 666 mRequestScoresLatch = new CountDownLatch(1); 667 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 668 assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 669 mRequestedKeys.clear(); 670 671 String ssid = "ssid3"; 672 String bssid = "00:00:00:00:00:00"; 673 ScanResult newResult = new ScanResult( 674 WifiSsid.fromUtf8Text(ssid), 675 bssid, 676 0, // hessid 677 0, //anqpDomainId 678 null, // osuProviders 679 "", // capabilities 680 RSSI_1, 681 0, // frequency 682 SystemClock.elapsedRealtime() * 1000); 683 when(mockWifiManager.getScanResults()) 684 .thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2(), newResult)); 685 686 mRequestScoresLatch = new CountDownLatch(1); 687 sendScanResults(tracker); 688 assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 689 690 assertEquals(1, mRequestedKeys.size()); 691 assertTrue(mRequestedKeys.contains(new NetworkKey(new WifiKey('"' + ssid + '"', bssid)))); 692 } 693 694 @Test scoreCacheAndListenerShouldBeUnregisteredWhenStopTrackingIsCalled()695 public void scoreCacheAndListenerShouldBeUnregisteredWhenStopTrackingIsCalled() throws Exception 696 { 697 WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); 698 WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue(); 699 700 tracker.onStop(); 701 verify(mockNetworkScoreManager).unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, cache); 702 703 // Verify listener is unregistered so updating a score does not throw an error by posting 704 // a message to the dead work handler 705 mWorkerThread.quit(); 706 sendUpdatedScores(); 707 } 708 709 /** 710 * Verify that tracking a Passpoint AP on a device with Passpoint disabled doesn't cause 711 * any crash. 712 * 713 * @throws Exception 714 */ 715 @Test trackPasspointApWithPasspointDisabled()716 public void trackPasspointApWithPasspointDisabled() throws Exception { 717 // TODO(sghuman): Delete this test and replace with a passpoint test 718 WifiTracker tracker = createMockedWifiTracker(); 719 720 // Add a Passpoint AP to the scan results. 721 List<ScanResult> results = new ArrayList<>(); 722 ScanResult passpointAp = new ScanResult( 723 WifiSsid.fromUtf8Text(SSID_1), 724 BSSID_1, 725 0, // hessid 726 0, //anqpDomainId 727 null, // osuProviders 728 "", // capabilities 729 RSSI_1, 730 0, // frequency 731 SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */); 732 passpointAp.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); 733 results.add(passpointAp); 734 735 // Update access point and verify UnsupportedOperationException is being caught for 736 // call to WifiManager#getMatchingWifiConfig. 737 when(mockWifiManager.getConfiguredNetworks()) 738 .thenReturn(new ArrayList<WifiConfiguration>()); 739 when(mockWifiManager.getScanResults()).thenReturn(results); 740 741 startTracking(tracker); 742 } 743 744 @Test rssiChangeBroadcastShouldUpdateConnectedAp()745 public void rssiChangeBroadcastShouldUpdateConnectedAp() throws Exception { 746 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 747 assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue(); 748 749 int newRssi = CONNECTED_RSSI + 10; 750 WifiInfo info = new WifiInfo(CONNECTED_AP_1_INFO); 751 info.setRssi(newRssi); 752 753 // Once the new info has been fetched, we need to wait for the access points to be copied 754 mAccessPointsChangedLatch = new CountDownLatch(1); 755 doAnswer(invocation -> info).when(mockWifiManager).getConnectionInfo(); 756 757 tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.RSSI_CHANGED_ACTION)); 758 759 assertTrue("onAccessPointsChanged never called", 760 mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 761 assertThat(tracker.getAccessPoints().get(0).getRssi()).isEqualTo(newRssi); 762 } 763 764 @Test onStartShouldSynchronouslyFetchLatestInformation()765 public void onStartShouldSynchronouslyFetchLatestInformation() throws Exception { 766 Network mockNetwork = mock(Network.class); 767 when(mockWifiManager.getCurrentNetwork()).thenReturn(mockNetwork); 768 769 when(mockWifiManager.getConnectionInfo()).thenReturn(CONNECTED_AP_1_INFO); 770 771 NetworkInfo networkInfo = new NetworkInfo( 772 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 773 networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test"); 774 when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(networkInfo); 775 776 WifiTracker tracker = createMockedWifiTracker(); 777 startTracking(tracker); 778 779 verify(mockWifiManager).getConnectionInfo(); 780 verify(mockWifiManager, times(1)).getConfiguredNetworks(); 781 verify(mockConnectivityManager).getNetworkInfo(any(Network.class)); 782 783 // mStaleAccessPoints is true 784 verify(mockWifiListener, never()).onAccessPointsChanged(); 785 assertThat(tracker.getAccessPoints().size()).isEqualTo(2); 786 assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue(); 787 } 788 789 @Test onStartShouldDisplayConnectedAccessPointWhenThereAreNoScanResults()790 public void onStartShouldDisplayConnectedAccessPointWhenThereAreNoScanResults() 791 throws Exception { 792 Network mockNetwork = mock(Network.class); 793 when(mockWifiManager.getCurrentNetwork()).thenReturn(mockNetwork); 794 795 when(mockWifiManager.getConnectionInfo()).thenReturn(CONNECTED_AP_1_INFO); 796 797 NetworkInfo networkInfo = new NetworkInfo( 798 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 799 networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test"); 800 when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(networkInfo); 801 802 // Don't return any scan results 803 when(mockWifiManager.getScanResults()).thenReturn(new ArrayList<>()); 804 805 WifiTracker tracker = createMockedWifiTracker(); 806 startTracking(tracker); 807 808 verify(mockWifiManager).getConnectionInfo(); 809 verify(mockWifiManager, times(1)).getConfiguredNetworks(); 810 verify(mockConnectivityManager).getNetworkInfo(any(Network.class)); 811 812 // mStaleAccessPoints is true 813 verify(mockWifiListener, never()).onAccessPointsChanged(); 814 815 assertThat(tracker.getAccessPoints()).hasSize(1); 816 assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue(); 817 } 818 819 @Test stopTrackingShouldRemoveAllPendingWork()820 public void stopTrackingShouldRemoveAllPendingWork() throws Exception { 821 WifiTracker tracker = createMockedWifiTracker(); 822 startTracking(tracker); 823 824 CountDownLatch ready = new CountDownLatch(1); 825 CountDownLatch latch = new CountDownLatch(1); 826 CountDownLatch lock = new CountDownLatch(1); 827 tracker.mWorkHandler.post(() -> { 828 try { 829 ready.countDown(); 830 lock.await(); 831 latch.countDown(); 832 } catch (InterruptedException e) { 833 fail("Interrupted Exception while awaiting lock release: " + e); 834 } 835 }); 836 837 // Enqueue messages 838 final AtomicBoolean executed = new AtomicBoolean(false); 839 tracker.mWorkHandler.post(() -> executed.set(true)); 840 841 try { 842 ready.await(); // Make sure we have entered the first message handler 843 } catch (InterruptedException e) {} 844 tracker.onStop(); 845 846 lock.countDown(); 847 assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 848 849 // In case the method was already executing 850 assertThat(tracker.mWorkHandler.hasMessagesOrCallbacks()).isFalse(); 851 852 assertThat(executed.get()).isFalse(); 853 } 854 855 @Test stopTrackingShouldPreventCallbacksFromOngoingWork()856 public void stopTrackingShouldPreventCallbacksFromOngoingWork() throws Exception { 857 WifiTracker tracker = createMockedWifiTracker(); 858 startTracking(tracker); 859 860 final CountDownLatch ready = new CountDownLatch(1); 861 final CountDownLatch latch = new CountDownLatch(1); 862 final CountDownLatch lock = new CountDownLatch(1); 863 tracker.mWorkHandler.post(() -> { 864 try { 865 ready.countDown(); 866 lock.await(); 867 868 tracker.mReceiver.onReceive( 869 mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); 870 871 latch.countDown(); 872 } catch (InterruptedException e) { 873 fail("Interrupted Exception while awaiting lock release: " + e); 874 } 875 }); 876 877 ready.await(); // Make sure we have entered the first message handler 878 tracker.onStop(); 879 lock.countDown(); 880 assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); 881 882 // Wait for main thread 883 final CountDownLatch latch2 = new CountDownLatch(1); 884 ThreadUtils.postOnMainThread(latch2::countDown); 885 latch2.await(); 886 887 verify(mockWifiListener, never()).onWifiStateChanged(anyInt()); 888 } 889 890 @Test stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult()891 public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult() 892 throws Exception { 893 WifiTracker tracker = createMockedWifiTracker(); 894 startTracking(tracker); 895 896 tracker.onStop(); 897 898 startTracking(tracker); 899 900 tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); 901 tracker.mReceiver.onReceive( 902 mContext, new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION)); 903 tracker.mReceiver.onReceive( 904 mContext, new Intent(WifiManager.ACTION_LINK_CONFIGURATION_CHANGED)); 905 906 907 verify(mockWifiListener, never()).onAccessPointsChanged(); 908 909 sendScanResults(tracker); // verifies onAccessPointsChanged is invoked 910 } 911 912 @Test startTrackingShouldNotSendAnyCallbacksUntilScanResultsAreProcessed()913 public void startTrackingShouldNotSendAnyCallbacksUntilScanResultsAreProcessed() 914 throws Exception { 915 WifiTracker tracker = createMockedWifiTracker(); 916 startTracking(tracker); 917 918 tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); 919 tracker.mReceiver.onReceive( 920 mContext, new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION)); 921 tracker.mReceiver.onReceive( 922 mContext, new Intent(WifiManager.ACTION_LINK_CONFIGURATION_CHANGED)); 923 924 verify(mockWifiListener, never()).onAccessPointsChanged(); 925 926 sendScanResults(tracker); // verifies onAccessPointsChanged is invoked 927 } 928 929 @Test disablingWifiShouldClearExistingAccessPoints()930 public void disablingWifiShouldClearExistingAccessPoints() throws Exception { 931 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 932 933 when(mockWifiManager.isWifiEnabled()).thenReturn(false); 934 935 mAccessPointsChangedLatch = new CountDownLatch(1); 936 tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); 937 assertThat(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)).isTrue(); 938 939 assertThat(tracker.getAccessPoints()).isEmpty(); 940 } 941 942 @Test onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange()943 public void onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange() throws Exception { 944 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 945 verify(mockWifiListener, times(1)).onConnectedChanged(); 946 947 NetworkInfo networkInfo = new NetworkInfo( 948 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 949 networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test"); 950 951 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 952 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); 953 tracker.mReceiver.onReceive(mContext, intent); 954 955 verify(mockWifiListener, times(1)).onConnectedChanged(); 956 } 957 958 @Test onConnectedChangedCallback_shouldBeInvokedWhenStateChanges()959 public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception { 960 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 961 verify(mockWifiListener, times(1)).onConnectedChanged(); 962 963 NetworkInfo networkInfo = new NetworkInfo( 964 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); 965 networkInfo.setDetailedState( 966 NetworkInfo.DetailedState.DISCONNECTED, "disconnected", "test"); 967 968 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 969 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); 970 tracker.mReceiver.onReceive(mContext, intent); 971 972 assertThat(tracker.isConnected()).isFalse(); 973 verify(mockWifiListener, times(2)).onConnectedChanged(); 974 } 975 976 @Test updateNetworkInfoWithNewConnectedNetwork_switchesNetworks()977 public void updateNetworkInfoWithNewConnectedNetwork_switchesNetworks() throws Exception { 978 WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); 979 980 switchToNetwork2(tracker); 981 982 List<AccessPoint> aps = tracker.getAccessPoints(); 983 assertThat(aps.get(0).getSsidStr()).isEqualTo(SSID_2); 984 985 assertThat(aps.get(0).isReachable()).isTrue(); 986 assertThat(aps.get(1).isReachable()).isTrue(); 987 } 988 989 @Test onStart_updateScanResults_evictOldScanResult()990 public void onStart_updateScanResults_evictOldScanResult() { 991 when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList( 992 buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(0))); 993 WifiTracker tracker = createMockedWifiTracker(); 994 995 tracker.forceUpdate(); 996 997 // Only has scanResult1 and scanResult2 998 assertThat(tracker.getAccessPoints()).hasSize(2); 999 assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); 1000 assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); 1001 } 1002 1003 /** 1004 * Verifies that a failed scan reported on SCAN_RESULTS_AVAILABLE_ACTION should increase the 1005 * ScanResult eviction timeout to twice the default. 1006 */ 1007 @Test failedScan_increasesEvictionTimeout()1008 public void failedScan_increasesEvictionTimeout() throws InterruptedException { 1009 when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList( 1010 buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp( 1011 SystemClock.elapsedRealtime() - WifiTracker.MAX_SCAN_RESULT_AGE_MILLIS))); 1012 WifiTracker tracker = createMockedWifiTracker(); 1013 1014 sendFailedScanResults(tracker); 1015 1016 // Failed scan increases timeout window to include the stale scan 1017 assertThat(tracker.getAccessPoints()).hasSize(3); 1018 assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); 1019 assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); 1020 assertThat(tracker.getAccessPoints().get(2).getBssid()).isEqualTo(BSSID_3); 1021 1022 sendScanResults(tracker); 1023 1024 // Successful scan resets the timeout window to remove the stale scan 1025 assertThat(tracker.getAccessPoints()).hasSize(2); 1026 assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); 1027 assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); 1028 } 1029 1030 /** 1031 * Verifies that updatePasspointAccessPoints will only return AccessPoints whose 1032 * isPasspoint() evaluates as true. 1033 */ 1034 @Test updatePasspointAccessPoints_returnedAccessPointsArePasspoint()1035 public void updatePasspointAccessPoints_returnedAccessPointsArePasspoint() { 1036 WifiTracker tracker = createMockedWifiTracker(); 1037 1038 List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( 1039 createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); 1040 1041 assertTrue(passpointAccessPoints.size() != 0); 1042 for (AccessPoint ap : passpointAccessPoints) { 1043 assertTrue(ap.isPasspoint()); 1044 } 1045 } 1046 1047 /** 1048 * Verifies that updatePasspointAccessPoints will return the same amount of AccessPoints as 1049 * unique WifiConfigurations, even if duplicate FQDNs exist. 1050 */ 1051 @Test updatePasspointAccessPoints_ignoresDuplicateFQDNs()1052 public void updatePasspointAccessPoints_ignoresDuplicateFQDNs() { 1053 WifiTracker tracker = createMockedWifiTracker(); 1054 1055 // Process matching list of four configs with two duplicate FQDNs. 1056 List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( 1057 createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); 1058 1059 // Should have 2 APs with unique FQDNs, ignoring the 2 duplicate FQDNs. 1060 assertThat(passpointAccessPoints).hasSize(2); 1061 1062 Set<String> fqdns = new ArraySet<>(Arrays.asList(FQDN_1, FQDN_2)); 1063 1064 assertTrue(fqdns.remove(passpointAccessPoints.get(0).getConfig().FQDN)); 1065 assertTrue(fqdns.remove(passpointAccessPoints.get(1).getConfig().FQDN)); 1066 } 1067 1068 /** 1069 * Verifies that updatePasspointAccessPoints will return matching cached APs and update their 1070 * scan results instead of creating new APs. 1071 */ 1072 @Test updatePasspointAccessPoints_usesCachedAccessPoints()1073 public void updatePasspointAccessPoints_usesCachedAccessPoints() { 1074 WifiTracker tracker = createMockedWifiTracker(); 1075 1076 ScanResult result = buildScanResult1(); 1077 1078 List<AccessPoint> passpointAccessPointsFirstUpdate = tracker.updatePasspointAccessPoints( 1079 createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), 1080 null, FQDN_1, PROVIDER_FRIENDLY_NAME_1), new ArrayList<>()); 1081 List<AccessPoint> cachedAccessPoints = new ArrayList<>(passpointAccessPointsFirstUpdate); 1082 1083 int prevRssi = result.level; 1084 int newRssi = prevRssi + 10; 1085 result.level = newRssi; 1086 1087 List<AccessPoint> passpointAccessPointsSecondUpdate = tracker.updatePasspointAccessPoints( 1088 createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), 1089 null, FQDN_1, PROVIDER_FRIENDLY_NAME_1), cachedAccessPoints); 1090 1091 // Verify second update AP is the same object as the first update AP 1092 assertThat(passpointAccessPointsFirstUpdate.get(0)) 1093 .isSameInstanceAs(passpointAccessPointsSecondUpdate.get(0)); 1094 // Verify second update AP has the average of the first and second update RSSIs 1095 assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi()) 1096 .isEqualTo((prevRssi + newRssi) / 2); 1097 } 1098 1099 /** 1100 * Verifies that the internal WifiConfiguration of a Passpoint AccessPoint is updated 1101 */ 1102 @Test updatePasspointAccessPoints_updatesConfig()1103 public void updatePasspointAccessPoints_updatesConfig() { 1104 WifiTracker tracker = createMockedWifiTracker(); 1105 1106 ScanResult result = buildScanResult1(); 1107 1108 List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( 1109 createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), 1110 null, FQDN_1, PROVIDER_FRIENDLY_NAME_1), new ArrayList<>()); 1111 1112 AccessPoint ap = passpointAccessPoints.get(0); 1113 assertEquals(ap.getTitle(), PROVIDER_FRIENDLY_NAME_1); 1114 1115 tracker.updatePasspointAccessPoints( 1116 createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), 1117 null, FQDN_1, PROVIDER_FRIENDLY_NAME_2), passpointAccessPoints); 1118 assertEquals(ap.getTitle(), PROVIDER_FRIENDLY_NAME_2); 1119 } 1120 1121 /** 1122 * Verifies that updateOsuAccessPoints will only return AccessPoints whose 1123 * isOsuProvider() evaluates as true. 1124 */ 1125 @Test updateOsuAccessPoints_returnedAccessPointsAreOsuProviders()1126 public void updateOsuAccessPoints_returnedAccessPointsAreOsuProviders() { 1127 WifiTracker tracker = createMockedWifiTracker(); 1128 1129 Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); 1130 providersAndScans.put( 1131 buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); 1132 providersAndScans.put( 1133 buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); 1134 1135 List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( 1136 providersAndScans, new ArrayList<>()); 1137 1138 assertThat(osuAccessPoints).hasSize(2); 1139 for (AccessPoint ap: osuAccessPoints) { 1140 assertThat(ap.isOsuProvider()).isTrue(); 1141 } 1142 } 1143 1144 /** 1145 * Verifies that updateOsuAccessPoints will not return Osu AccessPoints for already provisioned 1146 * networks 1147 */ 1148 @Test updateOsuAccessPoints_doesNotReturnAlreadyProvisionedOsuAccessPoints()1149 public void updateOsuAccessPoints_doesNotReturnAlreadyProvisionedOsuAccessPoints() { 1150 WifiTracker tracker = createMockedWifiTracker(); 1151 1152 // Start with two Osu Providers 1153 Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); 1154 providersAndScans.put( 1155 buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); 1156 providersAndScans.put( 1157 buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); 1158 1159 // First update 1160 List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( 1161 providersAndScans, new ArrayList<>()); 1162 1163 // Make sure both Osu Providers' APs are returned 1164 assertThat(osuAccessPoints).hasSize(2); 1165 List<String> friendlyNames = Arrays.asList( 1166 osuAccessPoints.get(0).getTitle(), osuAccessPoints.get(1).getTitle()); 1167 assertThat(friendlyNames) 1168 .containsExactly(PROVIDER_FRIENDLY_NAME_1, PROVIDER_FRIENDLY_NAME_2); 1169 1170 // Simulate Osu Provider 1 being provisioned 1171 Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigForOsuProvider = 1172 new HashMap<>(); 1173 matchingPasspointConfigForOsuProvider.put(buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), null); 1174 when(mockWifiManager.getMatchingPasspointConfigsForOsuProviders(any())).thenReturn( 1175 matchingPasspointConfigForOsuProvider); 1176 1177 // Second update 1178 osuAccessPoints = tracker.updateOsuAccessPoints( 1179 providersAndScans, new ArrayList<>()); 1180 1181 // Returned AP should only be for Osu Provider 2 1182 assertThat(osuAccessPoints).hasSize(1); 1183 assertThat(osuAccessPoints.get(0).getTitle()).isEqualTo(PROVIDER_FRIENDLY_NAME_2); 1184 } 1185 1186 /** 1187 * Verifies that updateOsuAccessPoints will return matching cached APs and update their 1188 * scan results instead of creating new APs. 1189 */ 1190 @Test updateOsuAccessPoints_usesCachedAccessPoints()1191 public void updateOsuAccessPoints_usesCachedAccessPoints() { 1192 WifiTracker tracker = createMockedWifiTracker(); 1193 1194 ScanResult result = buildScanResult1(); 1195 1196 Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); 1197 providersAndScans.put( 1198 buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(result)); 1199 1200 List<AccessPoint> osuAccessPointsFirstUpdate = tracker.updateOsuAccessPoints( 1201 providersAndScans, new ArrayList<>()); 1202 List<AccessPoint> cachedAccessPoints = new ArrayList<>(osuAccessPointsFirstUpdate); 1203 1204 // New RSSI for second update 1205 int prevRssi = result.level; 1206 int newRssi = prevRssi + 10; 1207 result.level = newRssi; 1208 1209 List<AccessPoint> osuAccessPointsSecondUpdate = tracker.updateOsuAccessPoints( 1210 providersAndScans, cachedAccessPoints); 1211 1212 // Verify second update AP is the same object as the first update AP 1213 assertThat(osuAccessPointsFirstUpdate.get(0)) 1214 .isSameInstanceAs(osuAccessPointsSecondUpdate.get(0)); 1215 // Verify second update AP has the average of the first and second update RSSIs 1216 assertThat(osuAccessPointsSecondUpdate.get(0).getRssi()) 1217 .isEqualTo((prevRssi + newRssi) / 2); 1218 } 1219 } 1220