summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorQuang Luong <qal@google.com>2019-10-30 13:39:35 -0700
committerEtan Cohen <etancohen@google.com>2019-11-15 12:58:23 -0800
commit229cd1062a1890286f3aad96bcc568e6d4509cc6 (patch)
tree85cb1bc5543149b5195ced822d5755a8399fa501 /libs
parent739153adae450723311a8e01fee6cadb6aaea003 (diff)
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
Diffstat (limited to 'libs')
-rw-r--r--libs/WifiTrackerLib/src/com/android/wifitrackerlib/NetworkDetailsTracker.java99
-rw-r--r--libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java149
-rw-r--r--libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java17
-rw-r--r--libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/NetworkDetailsTrackerTest.java114
-rw-r--r--libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/StandardNetworkDetailsTrackerTest.java253
5 files changed, 632 insertions, 0 deletions
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<WifiConfiguration> 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<BroadcastReceiver> 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);
+ }
+}