From 229cd1062a1890286f3aad96bcc568e6d4509cc6 Mon Sep 17 00:00:00 2001 From: Quang Luong Date: Wed, 30 Oct 2019 13:39:35 -0700 Subject: Created NetworkDetailsTracker for Network Details page Created new abstract class NetworkDetailsTracker to provide WifiEntry tracking functionality for a single network, for use in the Network Details page. Created subclass StandardNetworkDetailsTracker to implement the NetworkDetailsTracker for a tracked StandardWifiEntry. Bug: 70983952 Test: atest WifiTrackerLibTests Change-Id: Id2db7662567953bf1bc3a50b3ab4016a5d038129 --- .../wifitrackerlib/NetworkDetailsTracker.java | 99 ++++++++ .../StandardNetworkDetailsTracker.java | 149 ++++++++++++ .../android/wifitrackerlib/StandardWifiEntry.java | 17 ++ .../wifitrackerlib/NetworkDetailsTrackerTest.java | 114 ++++++++++ .../StandardNetworkDetailsTrackerTest.java | 253 +++++++++++++++++++++ 5 files changed, 632 insertions(+) create mode 100644 libs/WifiTrackerLib/src/com/android/wifitrackerlib/NetworkDetailsTracker.java create mode 100644 libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java create mode 100644 libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/NetworkDetailsTrackerTest.java create mode 100644 libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardNetworkDetailsTrackerTest.java (limited to 'libs') diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/NetworkDetailsTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/NetworkDetailsTracker.java new file mode 100644 index 000000000..e4d9bee67 --- /dev/null +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/NetworkDetailsTracker.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wifitrackerlib; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiManager; +import android.os.Handler; + +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; + +import java.time.Clock; + +/** + * Wi-Fi tracker that maintains a single WifiEntry corresponding to a given WifiEntry key. + */ +public abstract class NetworkDetailsTracker extends BaseWifiTracker { + /** + * Creates a concrete implementation of a NetworkDetailsTracker depending on the type of + * WifiEntry being tracked. + * + * @param lifecycle Lifecycle this is tied to for lifecycle callbacks. + * @param context Context for registering broadcast receiver and for resource + * strings. + * @param wifiManager Provides all Wi-Fi info. + * @param connectivityManager Provides network info. + * @param networkScoreManager Provides network scores for network badging. + * @param mainHandler Handler for processing listener callbacks. + * @param workerHandler Handler for processing all broadcasts and running the Scanner. + * @param clock Clock used for evaluating the age of scans + * @param maxScanAgeMillis Max age for tracked WifiEntries. + * @param scanIntervalMillis Interval between initiating scans. + * @param key Key of the WifiEntry to be tracked. + * @return + */ + public static NetworkDetailsTracker createNetworkDetailsTracker(@NonNull Lifecycle lifecycle, + @NonNull Context context, + @NonNull WifiManager wifiManager, + @NonNull ConnectivityManager connectivityManager, + @NonNull NetworkScoreManager networkScoreManager, + @NonNull Handler mainHandler, + @NonNull Handler workerHandler, + @NonNull Clock clock, + long maxScanAgeMillis, + long scanIntervalMillis, + String key) { + if (key.startsWith(StandardWifiEntry.KEY_PREFIX)) { + return new StandardNetworkDetailsTracker(lifecycle, context, wifiManager, + connectivityManager, networkScoreManager, mainHandler, workerHandler, clock, + maxScanAgeMillis, scanIntervalMillis, key); + } else { + throw new IllegalArgumentException("Key does not contain valid key prefix!"); + } + } + + /** + * Abstract constructor for NetworkDetailsTracker. + * Clients must use {@link NetworkDetailsTracker#createNetworkDetailsTracker} for creating + * an appropriate concrete instance of this class. + */ + NetworkDetailsTracker(@NonNull Lifecycle lifecycle, @NonNull Context context, + @NonNull WifiManager wifiManager, + @NonNull ConnectivityManager connectivityManager, + @NonNull NetworkScoreManager networkScoreManager, + @NonNull Handler mainHandler, + @NonNull Handler workerHandler, + @NonNull Clock clock, + long maxScanAgeMillis, + long scanIntervalMillis, + String tag) { + super(lifecycle, context, wifiManager, connectivityManager, networkScoreManager, + mainHandler, workerHandler, clock, maxScanAgeMillis, scanIntervalMillis, + null /* listener */, tag); + } + + /** + * Returns the WifiEntry object representing the single network being tracked. + */ + @AnyThread + @NonNull + public abstract WifiEntry getWifiEntry(); +} diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java new file mode 100644 index 000000000..6f8653f3c --- /dev/null +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wifitrackerlib; + +import static androidx.core.util.Preconditions.checkNotNull; + +import static com.android.wifitrackerlib.StandardWifiEntry.scanResultToStandardWifiEntryKey; +import static com.android.wifitrackerlib.StandardWifiEntry.wifiConfigToStandardWifiEntryKey; + +import static java.util.stream.Collectors.toList; + +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.text.TextUtils; + +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; + +import java.time.Clock; +import java.util.Collections; +import java.util.Optional; + +/** + * Implementation of NetworkDetailsTracker that tracks a single StandardWifiEntry. + */ +class StandardNetworkDetailsTracker extends NetworkDetailsTracker { + private static final String TAG = "StandardNetworkDetailsTracker"; + + private final StandardWifiEntry mChosenEntry; + + StandardNetworkDetailsTracker(@NonNull Lifecycle lifecycle, + @NonNull Context context, + @NonNull WifiManager wifiManager, + @NonNull ConnectivityManager connectivityManager, + @NonNull NetworkScoreManager networkScoreManager, + @NonNull Handler mainHandler, + @NonNull Handler workerHandler, + @NonNull Clock clock, + long maxScanAgeMillis, + long scanIntervalMillis, + String key) { + super(lifecycle, context, wifiManager, connectivityManager, networkScoreManager, + mainHandler, workerHandler, clock, maxScanAgeMillis, scanIntervalMillis, TAG); + mChosenEntry = new StandardWifiEntry(mMainHandler, key); + } + + @AnyThread + @Override + @NonNull + public WifiEntry getWifiEntry() { + return mChosenEntry; + } + + @Override + protected void handleOnStart() { + mScanResultUpdater.update(mWifiManager.getScanResults()); + conditionallyUpdateScanResults(true /* lastScanSucceeded */); + conditionallyUpdateConfig(); + } + + @Override + protected void handleWifiStateChangedAction() { + conditionallyUpdateScanResults(true /* lastScanSucceeded */); + } + + @Override + protected void handleScanResultsAvailableAction(@NonNull Intent intent) { + checkNotNull(intent, "Intent cannot be null!"); + conditionallyUpdateScanResults( + intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true)); + } + + @Override + protected void handleConfiguredNetworksChangedAction(@NonNull Intent intent) { + checkNotNull(intent, "Intent cannot be null!"); + final WifiConfiguration updatedConfig = + (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION); + if (updatedConfig != null && TextUtils.equals( + wifiConfigToStandardWifiEntryKey(updatedConfig), mChosenEntry.getKey())) { + final int changeReason = intent.getIntExtra(WifiManager.EXTRA_CHANGE_REASON, + -1 /* defaultValue*/); + if (changeReason == WifiManager.CHANGE_REASON_ADDED + || changeReason == WifiManager.CHANGE_REASON_CONFIG_CHANGE) { + mChosenEntry.updateConfig(updatedConfig); + } else if (changeReason == WifiManager.CHANGE_REASON_REMOVED) { + mChosenEntry.updateConfig(null); + } + } else { + conditionallyUpdateConfig(); + } + } + + /** + * Updates the tracked entry's scan results up to the max scan age (or more, if the last scan + * was unsuccessful). If Wifi is disabled, the tracked entry's level will be cleared. + */ + private void conditionallyUpdateScanResults(boolean lastScanSucceeded) { + if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED) { + mChosenEntry.updateScanResultInfo(Collections.emptyList()); + return; + } + + long scanAgeWindow = mMaxScanAgeMillis; + if (lastScanSucceeded) { + // Scan succeeded, cache new scans + mScanResultUpdater.update(mWifiManager.getScanResults().stream().filter( + scan -> TextUtils.equals( + scanResultToStandardWifiEntryKey(scan), mChosenEntry.getKey())) + .collect(toList())); + } else { + // Scan failed, increase scan age window to prevent WifiEntry list from + // clearing prematurely. + scanAgeWindow += mScanIntervalMillis; + } + mChosenEntry.updateScanResultInfo(mScanResultUpdater.getScanResults(scanAgeWindow)); + } + + /** + * Updates the tracked entry's WifiConfiguration from getConfiguredNetworks(), or sets it to + * null if it does not exist. + */ + private void conditionallyUpdateConfig() { + Optional optionalConfig = mWifiManager.getConfiguredNetworks() + .stream().filter(config -> TextUtils.equals( + wifiConfigToStandardWifiEntryKey(config), mChosenEntry.getKey())) + .findAny(); + mChosenEntry.updateConfig(optionalConfig.orElse(null)); + } +} diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java index fbebd93f6..9931acdeb 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java @@ -82,6 +82,23 @@ class StandardWifiEntry extends WifiEntry { mWifiConfig = config; } + StandardWifiEntry(@NonNull Handler callbackHandler, @NonNull String key) { + // TODO: second argument (isSaved = false) is bogus in this context + super(callbackHandler, false); + + if (!key.startsWith(KEY_PREFIX)) { + throw new IllegalArgumentException("Key does not start with correct prefix!"); + } + mKey = key; + try { + final int securityDelimiter = key.lastIndexOf(","); + mSsid = key.substring(KEY_PREFIX.length(), securityDelimiter); + mSecurity = Integer.valueOf(key.substring(securityDelimiter + 1)); + } catch (StringIndexOutOfBoundsException | NumberFormatException e) { + throw new IllegalArgumentException("Malformed key: " + key); + } + } + @Override public String getKey() { return mKey; diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/NetworkDetailsTrackerTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/NetworkDetailsTrackerTest.java new file mode 100644 index 000000000..b763d6577 --- /dev/null +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/NetworkDetailsTrackerTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wifitrackerlib; + +import static com.android.wifitrackerlib.NetworkDetailsTracker.createNetworkDetailsTracker; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.test.TestLooper; + +import androidx.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Clock; +import java.util.ArrayList; + +public class NetworkDetailsTrackerTest { + + private static final long START_MILLIS = 123_456_789; + + private static final long MAX_SCAN_AGE_MILLIS = 15_000; + private static final long SCAN_INTERVAL_MILLIS = 10_000; + + @Mock + private Lifecycle mMockLifecycle; + @Mock + private Context mMockContext; + @Mock + private WifiManager mMockWifiManager; + @Mock + private ConnectivityManager mMockConnectivityManager; + @Mock + private NetworkScoreManager mMockNetworkScoreManager; + @Mock + private Clock mMockClock; + + private TestLooper mTestLooper; + + private NetworkDetailsTracker createTestNetworkDetailsTracker(String key) { + final Handler testHandler = new Handler(mTestLooper.getLooper()); + + return createNetworkDetailsTracker(mMockLifecycle, mMockContext, + mMockWifiManager, + mMockConnectivityManager, + mMockNetworkScoreManager, + testHandler, + testHandler, + mMockClock, + MAX_SCAN_AGE_MILLIS, + SCAN_INTERVAL_MILLIS, + key); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTestLooper = new TestLooper(); + + when(mMockWifiManager.getScanResults()).thenReturn(new ArrayList<>()); + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED); + when(mMockClock.millis()).thenReturn(START_MILLIS); + } + + /** + * Tests that an invalid WifiEntry key passed into the constructor throws an exception. + */ + @Test + public void testCreateNetworkDetailsTracker_invalidKey_throwsError() { + try { + createTestNetworkDetailsTracker("Invalid Key"); + fail("Invalid key should have thrown an error!"); + } catch (IllegalArgumentException e) { + // Test succeeded + } + } + + /** + * Tests that createNetworkDetailsTracker() returns a StandardNetworkDetailsTracker if a + * StandardWifiEntry key is passed in. + */ + @Test + public void testCreateNetworkDetailsTracker_returnsStandardNetworkDetailsTracker() { + final NetworkDetailsTracker tracker = + createTestNetworkDetailsTracker(StandardWifiEntry.KEY_PREFIX + "ssid,0"); + assertThat(tracker).isInstanceOf(StandardNetworkDetailsTracker.class); + } +} diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardNetworkDetailsTrackerTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardNetworkDetailsTrackerTest.java new file mode 100644 index 000000000..740bbdcef --- /dev/null +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardNetworkDetailsTrackerTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wifitrackerlib; + +import static com.android.wifitrackerlib.StandardWifiEntry.scanResultToStandardWifiEntryKey; +import static com.android.wifitrackerlib.TestUtils.buildScanResult; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.test.TestLooper; + +import androidx.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Collections; + +public class StandardNetworkDetailsTrackerTest { + + private static final long START_MILLIS = 123_456_789; + + private static final long MAX_SCAN_AGE_MILLIS = 15_000; + private static final long SCAN_INTERVAL_MILLIS = 10_000; + + @Mock + private Lifecycle mMockLifecycle; + @Mock + private Context mMockContext; + @Mock + private WifiManager mMockWifiManager; + @Mock + private ConnectivityManager mMockConnectivityManager; + @Mock + private NetworkScoreManager mMockNetworkScoreManager; + @Mock + private Clock mMockClock; + + private TestLooper mTestLooper; + + private final ArgumentCaptor mBroadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + + private StandardNetworkDetailsTracker createTestStandardNetworkDetailsTracker(String key) { + final Handler testHandler = new Handler(mTestLooper.getLooper()); + + return new StandardNetworkDetailsTracker(mMockLifecycle, mMockContext, + mMockWifiManager, + mMockConnectivityManager, + mMockNetworkScoreManager, + testHandler, + testHandler, + mMockClock, + MAX_SCAN_AGE_MILLIS, + SCAN_INTERVAL_MILLIS, + key); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTestLooper = new TestLooper(); + + when(mMockWifiManager.getScanResults()).thenReturn(new ArrayList<>()); + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED); + when(mMockClock.millis()).thenReturn(START_MILLIS); + } + + /** + * Tests that a key without the correct prefix throws an error in the constructor. + */ + @Test + public void testConstructor_malformedPrefix_throwsError() { + final String key = "IncorrectPrefix:ssid,0"; + try { + final StandardNetworkDetailsTracker tracker = + createTestStandardNetworkDetailsTracker(key); + fail("Incorrect prefix in key argument should throw an error!"); + } catch (IllegalArgumentException e) { + // Test Succeeded + } + } + + /** + * Tests that a key without a security type throws an error in the constructor. + */ + @Test + public void testConstructor_malformedSecurity_throwsError() { + final String key = StandardWifiEntry.KEY_PREFIX + "ssid"; + try { + final StandardNetworkDetailsTracker tracker = + createTestStandardNetworkDetailsTracker(key); + fail("Incorrect security type in key argument should throw an error!"); + } catch (IllegalArgumentException e) { + // Test Succeeded + } + } + + /** + * Tests that the key of the created WifiEntry matches the key passed into the constructor. + */ + @Test + public void testGetWifiEntry_HasCorrectKey() { + final String key = scanResultToStandardWifiEntryKey( + buildScanResult("ssid", "bssid", START_MILLIS)); + + final StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(key); + + assertThat(tracker.getWifiEntry().getKey()).isEqualTo(key); + } + + /** + * Tests that SCAN_RESULTS_AVAILABLE_ACTION updates the level of the entry. + */ + @Test + public void testScanResultsAvailableAction_updates_getLevel() { + // Starting without any scans available should make level WIFI_LEVEL_UNREACHABLE + final ScanResult scan = buildScanResult("ssid", "bssid", START_MILLIS, -50 /* rssi */); + final String key = scanResultToStandardWifiEntryKey(scan); + final StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(key); + + tracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + final WifiEntry wifiEntry = tracker.getWifiEntry(); + + assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE); + + // Received fresh scan. Level should not be WIFI_LEVEL_UNREACHABLE anymore + when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(scan)); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) + .putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true)); + + assertThat(wifiEntry.getLevel()).isNotEqualTo(WIFI_LEVEL_UNREACHABLE); + + // Scan returned with no scans, old scans timed out. Level should be WIFI_LEVEL_UNREACHABLE. + when(mMockWifiManager.getScanResults()).thenReturn(Collections.emptyList()); + when(mMockClock.millis()).thenReturn(START_MILLIS + MAX_SCAN_AGE_MILLIS + 1); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) + .putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true)); + + assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE); + } + + /** + * Tests that CONFIGURED_NETWORKS_CHANGED_ACTION updates the isSaved() value of the entry. + */ + @Test + public void testConfiguredNetworksChangedAction_updates_isSaved() { + // Initialize with no config. isSaved() should return false. + final String key = scanResultToStandardWifiEntryKey( + buildScanResult("ssid", "bssid", START_MILLIS)); + final StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(key); + + tracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + final WifiEntry wifiEntry = tracker.getWifiEntry(); + + assertThat(wifiEntry.isSaved()).isFalse(); + + // Add a config via broadcast. isSaved() should return true. + final WifiConfiguration config = new WifiConfiguration(); + config.SSID = "\"" + "ssid" + "\""; + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION) + .putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, config) + .putExtra(WifiManager.EXTRA_CHANGE_REASON, + WifiManager.CHANGE_REASON_ADDED)); + + assertThat(wifiEntry.isSaved()).isTrue(); + + // Remove the config via broadcast. isSaved() should be false. + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION) + .putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, config) + .putExtra(WifiManager.EXTRA_CHANGE_REASON, + WifiManager.CHANGE_REASON_REMOVED)); + + assertThat(wifiEntry.isSaved()).isFalse(); + } + + /** + * Tests that WIFI_STATE_DISABLED will clear the scan results of the chosen entry regardless if + * the scan results are still valid. + */ + @Test + public void testWifiStateChanged_disabled_clearsLevel() { + // Start with scan result and wifi state enabled. Level should not be unreachable. + final ScanResult scan = buildScanResult("ssid", "bssid", START_MILLIS, -50 /* rssi */); + final String key = scanResultToStandardWifiEntryKey(scan); + when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(scan)); + + final StandardNetworkDetailsTracker tracker = createTestStandardNetworkDetailsTracker(key); + tracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + final WifiEntry wifiEntry = tracker.getWifiEntry(); + + assertThat(wifiEntry.getLevel()).isNotEqualTo(WIFI_LEVEL_UNREACHABLE); + + // Disable wifi. Level should be unreachable. + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + + assertThat(wifiEntry.getLevel()).isEqualTo(WIFI_LEVEL_UNREACHABLE); + } +} -- cgit v1.2.3