diff options
6 files changed, 666 insertions, 20 deletions
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java new file mode 100644 index 000000000..9a12ea3ba --- /dev/null +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java @@ -0,0 +1,327 @@ +/* + * 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.wifiConfigToStandardWifiEntryKey; + +import static java.util.stream.Collectors.groupingBy; + +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.text.TextUtils; +import android.util.Log; + +import androidx.annotation.AnyThread; +import androidx.annotation.GuardedBy; +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; +import androidx.lifecycle.Lifecycle; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Wi-Fi tracker that provides all Wi-Fi related data to the Saved Networks page. + * + * These include + * - List of WifiEntries for all saved networks, dynamically updated with ScanResults + * - List of WifiEntries for all saved subscriptions, dynamically updated with ScanResults + */ +public class SavedNetworkTracker extends BaseWifiTracker { + + private static final String TAG = "SavedNetworkTracker"; + + private final SavedNetworkTrackerCallback mListener; + + // Lock object for data returned by the public API + private final Object mLock = new Object(); + + @GuardedBy("mLock") private final List<WifiEntry> mSavedWifiEntries = new ArrayList<>(); + @GuardedBy("mLock") private final List<WifiEntry> mSubscriptionWifiEntries = new ArrayList<>(); + + // Cache containing visible StandardWifiEntries. Must be accessed only by the worker thread. + private final Map<String, StandardWifiEntry> mStandardWifiEntryCache = new HashMap<>(); + + public SavedNetworkTracker(@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, + @Nullable SavedNetworkTracker.SavedNetworkTrackerCallback listener) { + super(lifecycle, context, wifiManager, connectivityManager, networkScoreManager, + mainHandler, workerHandler, clock, maxScanAgeMillis, scanIntervalMillis, listener, + TAG); + mListener = listener; + } + + /** + * Returns a list of WifiEntries for all saved networks. If a network is in range, the + * corresponding WifiEntry will be updated with live ScanResult data. + * @return + */ + @AnyThread + @NonNull + public List<WifiEntry> getSavedWifiEntries() { + synchronized (mLock) { + return new ArrayList<>(mSavedWifiEntries); + } + } + + /** + * Returns a list of WifiEntries for all saved subscriptions. If a subscription network is in + * range, the corresponding WifiEntry will be updated with live ScanResult data. + * @return + */ + @AnyThread + @NonNull + public List<WifiEntry> getSubscriptionWifiEntries() { + synchronized (mLock) { + return new ArrayList<>(mSubscriptionWifiEntries); + } + } + + @WorkerThread + @Override + protected void handleOnStart() { + updateStandardWifiEntryConfigs(mWifiManager.getConfiguredNetworks()); + conditionallyUpdateScanResults(true /* lastScanSucceeded */); + updateSavedWifiEntries(); + updateSubscriptionWifiEntries(); + } + + @WorkerThread + @Override + protected void handleWifiStateChangedAction() { + conditionallyUpdateScanResults(true /* lastScanSucceeded */); + updateSavedWifiEntries(); + updateSubscriptionWifiEntries(); + } + + @WorkerThread + @Override + protected void handleScanResultsAvailableAction(@Nullable Intent intent) { + //TODO(b/70983952): Add PasspointWifiEntry and update their scans here. + checkNotNull(intent, "Intent cannot be null!"); + conditionallyUpdateScanResults(intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, + true /* defaultValue */)); + updateSavedWifiEntries(); + updateSubscriptionWifiEntries(); + } + + @WorkerThread + @Override + protected void handleConfiguredNetworksChangedAction(@Nullable Intent intent) { + checkNotNull(intent, "Intent cannot be null!"); + + final WifiConfiguration config = + (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION); + if (config != null) { + updateStandardWifiEntryConfig( + config, (Integer) intent.getExtra(WifiManager.EXTRA_CHANGE_REASON)); + } else { + updateStandardWifiEntryConfigs(mWifiManager.getConfiguredNetworks()); + } + updateSavedWifiEntries(); + } + + private void updateSavedWifiEntries() { + synchronized (mLock) { + mSavedWifiEntries.clear(); + mSavedWifiEntries.addAll(mStandardWifiEntryCache.values()); + Collections.sort(mSavedWifiEntries); + if (isVerboseLoggingEnabled()) { + Log.v(TAG, "Updated SavedWifiEntries: " + + Arrays.toString(mSavedWifiEntries.toArray())); + } + } + notifyOnSavedWifiEntriesChanged(); + } + + private void updateSubscriptionWifiEntries() { + synchronized (mLock) { + mSubscriptionWifiEntries.clear(); + // TODO(b/70983952): Implement PasspointWifiEntry and add here + // mSubscriptionWifiEntries.addAll(mPasspointWifiEntryCache.values()); + Collections.sort(mSubscriptionWifiEntries); + if (isVerboseLoggingEnabled()) { + Log.v(TAG, "Updated SubscriptionWifiEntries: " + + Arrays.toString(mSubscriptionWifiEntries.toArray())); + } + } + notifyOnSubscriptionWifiEntriesChanged(); + } + + private void updateStandardWifiEntryScans(@NonNull List<ScanResult> scanResults) { + checkNotNull(scanResults, "Scan Result list should not be null!"); + + // Group scans by StandardWifiEntry key + final Map<String, List<ScanResult>> scanResultsByKey = scanResults.stream() + .filter(scanResult -> !TextUtils.isEmpty(scanResult.SSID)) + .collect(groupingBy(StandardWifiEntry::scanResultToStandardWifiEntryKey)); + + // Iterate through current entries and update each entry's scan results + mStandardWifiEntryCache.entrySet().forEach(entry -> { + final String key = entry.getKey(); + final StandardWifiEntry wifiEntry = entry.getValue(); + // Update scan results if available, or set to null. + wifiEntry.updateScanResultInfo(scanResultsByKey.get(key)); + }); + } + + /** + * Conditionally updates the WifiEntry scan results based on the current wifi state and + * whether the last scan succeeded or not. + */ + @WorkerThread + private void conditionallyUpdateScanResults(boolean lastScanSucceeded) { + if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED) { + updateStandardWifiEntryScans(Collections.emptyList()); + return; + } + + long scanAgeWindow = mMaxScanAgeMillis; + if (lastScanSucceeded) { + // Scan succeeded, cache new scans + mScanResultUpdater.update(mWifiManager.getScanResults()); + } else { + // Scan failed, increase scan age window to prevent WifiEntry list from + // clearing prematurely. + scanAgeWindow += mScanIntervalMillis; + } + updateStandardWifiEntryScans(mScanResultUpdater.getScanResults(scanAgeWindow)); + } + + /** + * Updates or removes a WifiConfiguration for the corresponding StandardWifiEntry if it exists. + * + * If an entry does not exist and the changeReason is ADDED or UPDATED, then a new entry will + * be created for the new config. + * + * @param config WifiConfiguration to update + * @param changeReason WifiManager.CHANGE_REASON_ADDED, WifiManager.CHANGE_REASON_REMOVED, or + * WifiManager.CHANGE_REASON_CONFIG_CHANGE + */ + @WorkerThread + private void updateStandardWifiEntryConfig(@NonNull WifiConfiguration config, + int changeReason) { + checkNotNull(config, "Config should not be null!"); + + final String key = wifiConfigToStandardWifiEntryKey(config); + final StandardWifiEntry entry = mStandardWifiEntryCache.get(key); + + if (entry != null) { + if (changeReason == WifiManager.CHANGE_REASON_REMOVED) { + entry.updateConfig(null); + mStandardWifiEntryCache.remove(key); + } else { // CHANGE_REASON_ADDED || CHANGE_REASON_CONFIG_CHANGE + entry.updateConfig(config); + } + } else { + if (changeReason != WifiManager.CHANGE_REASON_REMOVED) { + mStandardWifiEntryCache.put(key, new StandardWifiEntry(mMainHandler, config)); + } + } + } + + private void updateStandardWifiEntryConfigs(@NonNull List<WifiConfiguration> configs) { + checkNotNull(configs, "Config list should not be null!"); + + // Group configs by StandardWifiEntry key + final Map<String, WifiConfiguration> wifiConfigsByKey = + configs.stream().collect(Collectors.toMap( + StandardWifiEntry::wifiConfigToStandardWifiEntryKey, + Function.identity())); + + // Iterate through current entries and update each entry's config + mStandardWifiEntryCache.entrySet().removeIf((entry) -> { + final StandardWifiEntry wifiEntry = entry.getValue(); + final String key = wifiEntry.getKey(); + // Update config if available, or set to null (unsaved) + wifiEntry.updateConfig(wifiConfigsByKey.remove(key)); + // Entry is now unsaved, remove it. + return !wifiEntry.isSaved(); + }); + + // Create new entry for each unmatched config + for (String key : wifiConfigsByKey.keySet()) { + mStandardWifiEntryCache.put(key, + new StandardWifiEntry(mMainHandler, wifiConfigsByKey.get(key))); + } + } + + /** + * Posts onSavedWifiEntriesChanged callback on the main thread. + */ + @WorkerThread + private void notifyOnSavedWifiEntriesChanged() { + if (mListener != null) { + mMainHandler.post(mListener::onSavedWifiEntriesChanged); + } + } + + /** + * Posts onSubscriptionWifiEntriesChanged callback on the main thread. + */ + @WorkerThread + private void notifyOnSubscriptionWifiEntriesChanged() { + if (mListener != null) { + mMainHandler.post(mListener::onSubscriptionWifiEntriesChanged); + } + } + + /** + * Listener for changes to the list of saved and subscription WifiEntries + * + * These callbacks must be run on the MainThread. + */ + public interface SavedNetworkTrackerCallback extends BaseWifiTracker.BaseWifiTrackerCallback { + /** + * Called when there are changes to + * {@link #getSavedWifiEntries()} + */ + @MainThread + void onSavedWifiEntriesChanged(); + + /** + * Called when there are changes to + * {@link #getSubscriptionWifiEntries()} + */ + @MainThread + void onSubscriptionWifiEntriesChanged(); + } +} diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java index 54076dbe1..fbebd93f6 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java @@ -56,7 +56,7 @@ class StandardWifiEntry extends WifiEntry { StandardWifiEntry(@NonNull Handler callbackHandler, @NonNull List<ScanResult> scanResults) throws IllegalArgumentException { - super(callbackHandler); + super(callbackHandler, false /* forSavedNetworksPage */); checkNotNull(scanResults, "Cannot construct with null ScanResult list!"); if (scanResults.isEmpty()) { @@ -71,7 +71,7 @@ class StandardWifiEntry extends WifiEntry { StandardWifiEntry(@NonNull Handler callbackHandler, @NonNull WifiConfiguration config) throws IllegalArgumentException { - super(callbackHandler); + super(callbackHandler, true /* forSavedNetworksPage */); checkNotNull(config, "Cannot construct with null config!"); checkNotNull(config.SSID, "Supplied config must have an SSID!"); @@ -102,6 +102,8 @@ class StandardWifiEntry extends WifiEntry { @Override public String getSummary() { // TODO(b/70983952): Fill this method in + if (mForSavedNetworksPage) return null; + if (isSaved()) return "Saved"; // Placeholder for visual verification return null; } diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java index 94fbdf278..aa8ece7fa 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java @@ -126,13 +126,17 @@ public abstract class WifiEntry implements Comparable<WifiEntry> { public static final int FREQUENCY_6_GHZ = 6_000; public static final int FREQUENCY_UNKNOWN = -1; + protected final boolean mForSavedNetworksPage; + // Callback associated with this WifiEntry. Subclasses should call its methods appropriately. private WifiEntryCallback mListener; private Handler mCallbackHandler; - WifiEntry(@NonNull Handler callbackHandler) throws IllegalArgumentException { + WifiEntry(@NonNull Handler callbackHandler, boolean forSavedNetworksPage) + throws IllegalArgumentException { checkNotNull(callbackHandler, "Cannot construct with null handler!"); mCallbackHandler = callbackHandler; + mForSavedNetworksPage = forSavedNetworksPage; } // Info available for all WifiEntries // diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/SavedNetworkTrackerTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/SavedNetworkTrackerTest.java new file mode 100644 index 000000000..e7e7cbde1 --- /dev/null +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/SavedNetworkTrackerTest.java @@ -0,0 +1,306 @@ +/* + * 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.TestUtils.buildScanResult; +import static com.android.wifitrackerlib.TestUtils.buildWifiConfiguration; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +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.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.Arrays; +import java.util.Collections; +import java.util.stream.Collectors; + +public class SavedNetworkTrackerTest { + + 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; + @Mock + private SavedNetworkTracker.SavedNetworkTrackerCallback mMockCallback; + + private TestLooper mTestLooper; + + private final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + + private SavedNetworkTracker createTestSavedNetworkTracker() { + final Handler testHandler = new Handler(mTestLooper.getLooper()); + + return new SavedNetworkTracker(mMockLifecycle, mMockContext, + mMockWifiManager, + mMockConnectivityManager, + mMockNetworkScoreManager, + testHandler, + testHandler, + mMockClock, + MAX_SCAN_AGE_MILLIS, + SCAN_INTERVAL_MILLIS, + mMockCallback); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTestLooper = new TestLooper(); + + when(mMockWifiManager.getScanResults()).thenReturn(new ArrayList<>()); + when(mMockClock.millis()).thenReturn(START_MILLIS); + } + + /** + * Tests that the wifi state is set correctly after onStart, even if no broadcast was received. + */ + @Test + public void testOnStart_setsWifiState() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + + // Set the wifi state to disabled + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED); + savedNetworkTracker.onStart(); + mTestLooper.dispatchAll(); + + assertThat(savedNetworkTracker.getWifiState()).isEqualTo(WifiManager.WIFI_STATE_DISABLED); + + // Change the wifi state to enabled + savedNetworkTracker.onStop(); + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED); + savedNetworkTracker.onStart(); + mTestLooper.dispatchAll(); + + assertThat(savedNetworkTracker.getWifiState()).isEqualTo(WifiManager.WIFI_STATE_ENABLED); + } + + /** + * Tests that receiving a wifi state change broadcast updates getWifiState(). + */ + @Test + public void testWifiStateChangeBroadcast_updatesWifiState() { + final SavedNetworkTracker wifiPickerTracker = createTestSavedNetworkTracker(); + wifiPickerTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + + // Set the wifi state to disabled + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + + assertThat(wifiPickerTracker.getWifiState()).isEqualTo(WifiManager.WIFI_STATE_DISABLED); + + // Change the wifi state to enabled + when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + + assertThat(wifiPickerTracker.getWifiState()).isEqualTo(WifiManager.WIFI_STATE_ENABLED); + } + + /** + * Tests that receiving a wifi state change broadcast notifies the listener. + */ + @Test + public void testWifiStateChangeBroadcast_notifiesListener() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + mTestLooper.dispatchAll(); + + verify(mMockCallback, atLeastOnce()).onWifiStateChanged(); + } + + /** + * Tests that a CONFIGURED_NETWORKS_CHANGED broadcast notifies the listener for + * onSavedWifiEntriesChanged(). + */ + @Test + public void testConfiguredNetworksChanged_notifiesListener() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION)); + mTestLooper.dispatchAll(); + + verify(mMockCallback, atLeastOnce()).onSavedWifiEntriesChanged(); + } + + /** + * Tests that a WifiEntry is created for each configured network for getSavedWifiEntries(). + */ + @Test + public void testGetSavedWifiEntries_onStart_entryForEachConfiguredNetwork() { + when(mMockWifiManager.getConfiguredNetworks()).thenReturn(Arrays.asList( + buildWifiConfiguration("ssid0"), + buildWifiConfiguration("ssid1"), + buildWifiConfiguration("ssid2") + )); + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + + assertThat(savedNetworkTracker.getSavedWifiEntries().stream().map(WifiEntry::getTitle) + .collect(Collectors.toSet())) + .containsExactly("ssid0", "ssid1", "ssid2"); + } + + /** + * Tests that a CONFIGURED_NETWORKS_CHANGED broadcast with CHANGE_REASON_ADDED + * adds the corresponding WifiEntry from getSavedWifiEntries(). + */ + @Test + public void testGetSavedWifiEntries_configuredNetworksChanged_addsEntry() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + + assertThat(savedNetworkTracker.getSavedWifiEntries()).isEmpty(); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION) + .putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, + buildWifiConfiguration("ssid")) + .putExtra(WifiManager.EXTRA_CHANGE_REASON, + WifiManager.CHANGE_REASON_ADDED)); + + assertThat(savedNetworkTracker.getSavedWifiEntries()).hasSize(1); + } + + /** + * Tests that a CONFIGURED_NETWORKS_CHANGED broadcast with CHANGE_REASON_REMOVED + * removes the corresponding WifiEntry from getSavedWifiEntries(). + */ + @Test + public void testGetSavedWifiEntries_configuredNetworksChanged_removesEntry() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + final WifiConfiguration config = buildWifiConfiguration("ssid"); + when(mMockWifiManager.getConfiguredNetworks()) + .thenReturn(Collections.singletonList(config)); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + + assertThat(savedNetworkTracker.getSavedWifiEntries()).hasSize(1); + + 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(savedNetworkTracker.getSavedWifiEntries()).isEmpty(); + } + + /** + * Tests that receiving a scan results available broadcast notifies the listener. + */ + @Test + public void testScanResultsAvailableAction_notifiesListener() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + mTestLooper.dispatchAll(); + + verify(mMockCallback, atLeastOnce()).onSavedWifiEntriesChanged(); + } + + /** + * Tests that the scan results available broadcast changes the level of saved WifiEntries. + */ + @Test + public void testGetSavedWifiEntries_scanResultsAvailableAction_changesLevel() { + final SavedNetworkTracker savedNetworkTracker = createTestSavedNetworkTracker(); + final WifiConfiguration config = buildWifiConfiguration("ssid"); + when(mMockWifiManager.getConfiguredNetworks()) + .thenReturn(Collections.singletonList(config)); + savedNetworkTracker.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + final WifiEntry entry = savedNetworkTracker.getSavedWifiEntries().get(0); + + assertThat(entry.getLevel()).isEqualTo(WifiEntry.WIFI_LEVEL_UNREACHABLE); + + when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList( + buildScanResult("ssid", "bssid", START_MILLIS, -50 /* rssi */))); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + + assertThat(entry.getLevel()).isNotEqualTo(WifiEntry.WIFI_LEVEL_UNREACHABLE); + + when(mMockClock.millis()).thenReturn(START_MILLIS + MAX_SCAN_AGE_MILLIS + 1); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + + assertThat(entry.getLevel()).isEqualTo(WifiEntry.WIFI_LEVEL_UNREACHABLE); + } +} diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/TestUtils.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/TestUtils.java index 547a5721e..9f5b5433a 100644 --- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/TestUtils.java +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/TestUtils.java @@ -17,6 +17,7 @@ package com.android.wifitrackerlib; import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; /** * Utility methods for testing purposes. @@ -38,4 +39,10 @@ class TestUtils { result.level = rssi; return result; } + + static WifiConfiguration buildWifiConfiguration(String ssid) { + final WifiConfiguration config = new WifiConfiguration(); + config.SSID = "\"" + ssid + "\""; + return config; + } } diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java index 3fd18f98c..d298d4872 100644 --- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java @@ -70,14 +70,14 @@ public class WifiPickerTrackerTest { @Mock private Clock mMockClock; @Mock - private WifiPickerTracker.WifiPickerTrackerCallback mMockWifiTrackerCallback; + private WifiPickerTracker.WifiPickerTrackerCallback mMockCallback; private TestLooper mTestLooper; private final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - private WifiPickerTracker createTestWifiTracker2() { + private WifiPickerTracker createTestWifiPickerTracker() { final Handler testHandler = new Handler(mTestLooper.getLooper()); return new WifiPickerTracker(mMockLifecycle, mMockContext, @@ -89,7 +89,7 @@ public class WifiPickerTrackerTest { mMockClock, MAX_SCAN_AGE_MILLIS, SCAN_INTERVAL_MILLIS, - mMockWifiTrackerCallback); + mMockCallback); } @Before @@ -107,7 +107,7 @@ public class WifiPickerTrackerTest { */ @Test public void testWifiStateChangeBroadcast_updatesWifiState() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -133,7 +133,7 @@ public class WifiPickerTrackerTest { */ @Test public void testWifiStateChangeBroadcast_notifiesListener() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -142,7 +142,7 @@ public class WifiPickerTrackerTest { new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); mTestLooper.dispatchAll(); - verify(mMockWifiTrackerCallback, atLeastOnce()).onWifiStateChanged(); + verify(mMockCallback, atLeastOnce()).onWifiStateChanged(); } /** @@ -151,7 +151,7 @@ public class WifiPickerTrackerTest { */ @Test public void testConfiguredNetworksChanged_notifiesListener() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -160,7 +160,7 @@ public class WifiPickerTrackerTest { new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION)); mTestLooper.dispatchAll(); - verify(mMockWifiTrackerCallback, atLeastOnce()).onNumSavedNetworksChanged(); + verify(mMockCallback, atLeastOnce()).onNumSavedNetworksChanged(); } /** @@ -168,7 +168,7 @@ public class WifiPickerTrackerTest { */ @Test public void testOnStart_setsWifiState() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); // Set the wifi state to disabled when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED); @@ -191,7 +191,7 @@ public class WifiPickerTrackerTest { */ @Test public void testScanResultsAvailableAction_notifiesListener() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -200,7 +200,7 @@ public class WifiPickerTrackerTest { new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); mTestLooper.dispatchAll(); - verify(mMockWifiTrackerCallback, atLeastOnce()).onWifiEntriesChanged(); + verify(mMockCallback, atLeastOnce()).onWifiEntriesChanged(); } /** @@ -208,7 +208,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_noScans_emptyList() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -228,7 +228,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_wifiNetworkEntries_createdForEachSsidAndSecurityPair() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -263,7 +263,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_wifiNetworkEntries_oldEntriesTimedOut() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -300,7 +300,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_wifiNetworkEntries_useOldEntriesOnFailedScan() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -340,7 +340,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_configuredNetworksChanged_unsavedToSaved() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); wifiPickerTracker.onStart(); verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); @@ -370,7 +370,7 @@ public class WifiPickerTrackerTest { */ @Test public void testGetWifiEntries_configuredNetworksChanged_savedToUnsaved() { - final WifiPickerTracker wifiPickerTracker = createTestWifiTracker2(); + final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker(); final WifiConfiguration config = new WifiConfiguration(); config.SSID = "\"ssid\""; when(mMockWifiManager.getConfiguredNetworks()) |