diff options
author | Quang Luong <qal@google.com> | 2019-10-04 12:03:50 -0700 |
---|---|---|
committer | Quang Luong <qal@google.com> | 2019-10-14 13:33:50 -0700 |
commit | ae497007d38ebd35c04735434055c21d22140c38 (patch) | |
tree | bade7c463241c5aa6fe28b2aae84c2b6e45c5140 /libs | |
parent | 8f16a723984420c3fa321ea9345c60f2bbf0c4c3 (diff) |
Update saved status for StandardWifiEntries
Visible StandardWifiEntries (returned by getWifiEntries() API and shown
as normal wifi networks in the picker) will be updated with their
WifiConfiguration through WifiManager.getConfiguredNetworks() or the
CONFIGURED_NETWORK_CHANGED broadcasts.
StandardWifiEntries for getSavedWifiEntries() will be handled in a
different CL.
Bug: 70983952
Test: atest WifiTrackerLibTest -- visual verification by saving
GoogleGuest, switching to WifiTracker2, and verifying "Saved" in
GoogleGuest's summary.
Change-Id: I16c912a1d86394011475fec28f9c1853932d196d
Diffstat (limited to 'libs')
3 files changed, 211 insertions, 57 deletions
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java index af7df4d9c..54076dbe1 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java @@ -63,7 +63,7 @@ class StandardWifiEntry extends WifiEntry { throw new IllegalArgumentException("Cannot construct with empty ScanResult list!"); } final ScanResult firstScan = scanResults.get(0); - mKey = createStandardWifiEntryKey(firstScan); + mKey = scanResultToStandardWifiEntryKey(firstScan); mSsid = firstScan.SSID; mSecurity = getSecurityFromScanResult(firstScan); updateScanResultInfo(scanResults); @@ -76,7 +76,7 @@ class StandardWifiEntry extends WifiEntry { checkNotNull(config, "Cannot construct with null config!"); checkNotNull(config.SSID, "Supplied config must have an SSID!"); - mKey = createStandardWifiEntryKey(config); + mKey = wifiConfigToStandardWifiEntryKey(config); mSsid = removeDoubleQuotes(config.SSID); mSecurity = getSecurityFromWifiConfiguration(config); mWifiConfig = config; @@ -102,6 +102,7 @@ class StandardWifiEntry extends WifiEntry { @Override public String getSummary() { // TODO(b/70983952): Fill this method in + if (isSaved()) return "Saved"; // Placeholder for visual verification return null; } @@ -266,9 +267,9 @@ class StandardWifiEntry extends WifiEntry { } @WorkerThread - void updateScanResultInfo(@NonNull List<ScanResult> scanResults) + void updateScanResultInfo(@Nullable List<ScanResult> scanResults) throws IllegalArgumentException { - checkNotNull(scanResults); + if (scanResults == null) scanResults = new ArrayList<>(); for (ScanResult result : scanResults) { if (!TextUtils.equals(result.SSID, mSsid)) { @@ -322,13 +323,13 @@ class StandardWifiEntry extends WifiEntry { } @NonNull - static String createStandardWifiEntryKey(@NonNull ScanResult scan) { + static String scanResultToStandardWifiEntryKey(@NonNull ScanResult scan) { checkNotNull(scan, "Cannot create key with null scan result!"); return KEY_PREFIX + scan.SSID + "," + getSecurityFromScanResult(scan); } @NonNull - static String createStandardWifiEntryKey(@NonNull WifiConfiguration config) { + static String wifiConfigToStandardWifiEntryKey(@NonNull WifiConfiguration config) { checkNotNull(config, "Cannot create key with null config!"); checkNotNull(config.SSID, "Cannot create key with null SSID in config!"); return KEY_PREFIX + removeDoubleQuotes(config.SSID) + "," diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTracker2.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTracker2.java index a3cfa8af9..3feab63d6 100644 --- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTracker2.java +++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTracker2.java @@ -16,9 +16,13 @@ package com.android.wifitrackerlib; -import static com.android.wifitrackerlib.StandardWifiEntry.createStandardWifiEntryKey; +import static androidx.core.util.Preconditions.checkNotNull; + +import static com.android.wifitrackerlib.StandardWifiEntry.wifiConfigToStandardWifiEntryKey; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE; import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; import android.content.BroadcastReceiver; import android.content.Context; @@ -27,6 +31,7 @@ import android.content.IntentFilter; 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.Looper; @@ -50,6 +55,8 @@ 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; /** * Keeps track of the state of Wi-Fi and supplies {@link WifiEntry} for use in Wi-Fi picker lists. @@ -113,21 +120,42 @@ public class WifiTracker2 implements LifecycleObserver { } notifyOnWifiStateChanged(); } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { - final boolean lastScanSucceeded = - intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true); - if (lastScanSucceeded) updateScanResults(); - updateWifiEntries(lastScanSucceeded); - notifyOnWifiEntriesChanged(); + if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true)) { + // Scan succeeded, update scans aged to mMaxScanAgeMillis + mScanResultUpdater.update(mWifiManager.getScanResults()); + updateStandardWifiEntryScans(mScanResultUpdater.getScanResults( + mMaxScanAgeMillis)); + } else { + // Scan failed, keep results from previous scan to prevent WifiEntry list from + // clearing prematurely. + updateStandardWifiEntryScans(mScanResultUpdater.getScanResults( + mMaxScanAgeMillis + mScanIntervalMillis)); + } + updateWifiEntries(); + } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)) { + 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()); + } + updateWifiEntries(); } } }; private final ScanResultUpdater mScanResultUpdater; private final Scanner mScanner; - // Lock object for mWifiEntries + // Lock object for data returned by the public API private final Object mLock = new Object(); + // List representing return value of the getWifiEntries() API @GuardedBy("mLock") private final List<WifiEntry> mWifiEntries = new ArrayList<>(); + + // Cache containing StandardWifiEntries for visible networks and saved networks + // Must be accessed only by the worker thread. private final Map<String, StandardWifiEntry> mStandardWifiEntryCache = new HashMap<>(); /** @@ -195,9 +223,10 @@ public class WifiTracker2 implements LifecycleObserver { } notifyOnWifiStateChanged(); - updateScanResults(); - updateWifiEntries(true); - notifyOnWifiEntriesChanged(); + mScanResultUpdater.update(mWifiManager.getScanResults()); + updateStandardWifiEntryScans(mScanResultUpdater.getScanResults(mMaxScanAgeMillis)); + updateStandardWifiEntryConfigs(mWifiManager.getConfiguredNetworks()); + updateWifiEntries(); }); } @@ -266,62 +295,122 @@ public class WifiTracker2 implements LifecycleObserver { return new ArrayList<>(); } + /** + * Update the list returned by getWifiEntries() with the current states of the entry caches. + */ @WorkerThread - private void updateScanResults() { - mScanResultUpdater.update(mWifiManager.getScanResults()); - if (isVerboseLoggingEnabled()) { - Log.v(TAG, "Updated scans: " + Arrays.toString( - mScanResultUpdater.getScanResults(mMaxScanAgeMillis).toArray())); - } - } - - @WorkerThread - private void updateWifiEntries(boolean lastScanSucceeded) { - updateStandardWifiEntryCache(lastScanSucceeded); - // TODO (b/70983952): Update Passpoint/Suggestion entries here. - // updatePasspointWifiEntries(); - // updateCarrierWifiEntries(); - // updateSuggestionWifiEntries(); + private void updateWifiEntries() { synchronized (mLock) { mWifiEntries.clear(); - mWifiEntries.addAll(mStandardWifiEntryCache.values()); - // mWifiEntries.addAll(mPasspointWifiEntries); - // mWifiEntries.addAll(mCarrierWifiEntries); - // mWifiEntries.addAll(mSuggestionWifiEntries); + mWifiEntries.addAll(mStandardWifiEntryCache.values().stream().filter( + entry -> entry.getLevel() != WIFI_LEVEL_UNREACHABLE).collect(toList())); + // mWifiEntries.addAll(mPasspointWifiEntryCache); + // mWifiEntries.addAll(mCarrierWifiEntryCache); + // mWifiEntries.addAll(mSuggestionWifiEntryCache); Collections.sort(mWifiEntries); if (isVerboseLoggingEnabled()) { Log.v(TAG, "Updated WifiEntries: " + Arrays.toString(mWifiEntries.toArray())); } } + notifyOnWifiEntriesChanged(); + } + + /** + * Updates or removes a single WifiConfiguration for the corresponding StandardWifiEntry. + * A new entry will be created for configs without an existing entry. + * Unsaved and unreachable entries will be removed. + * + * @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(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); + // Remove unsaved, unreachable entry from cache + if (entry.getLevel() == WIFI_LEVEL_UNREACHABLE) 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)); + } + } } /** - * Updates mStandardWifiEntryCache with fresh scans. + * Updates or removes all saved WifiConfigurations for the corresponding StandardWifiEntries. + * New entries will be created for configs without an existing entry. + * Unsaved and unreachable entries will be removed. + * + * @param configs List of saved WifiConfigurations */ @WorkerThread - private void updateStandardWifiEntryCache(boolean lastScanSucceeded) { - // If the current scan failed, use results from the previous scan to prevent flicker. - final List<ScanResult> scanResults = mScanResultUpdater.getScanResults( - lastScanSucceeded ? mMaxScanAgeMillis : mMaxScanAgeMillis + mScanIntervalMillis); - final Map<String, StandardWifiEntry> updatedStandardWifiEntries = new HashMap<>(); + 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(e -> { + final String key = e.getKey(); + final StandardWifiEntry entry = e.getValue(); + // Update the config if available, or set to null. + entry.updateConfig(wifiConfigsByKey.remove(key)); + // Entry is not saved and is unreachable, remove it. + return !entry.isSaved() && entry.getLevel() == WIFI_LEVEL_UNREACHABLE; + }); + + // Create new StandardWifiEntry objects for each leftover config + for (Map.Entry<String, WifiConfiguration> e: wifiConfigsByKey.entrySet()) { + mStandardWifiEntryCache.put(e.getKey(), + new StandardWifiEntry(mMainHandler, e.getValue())); + } + } - // Group scans by StandardWifiEntry Key + /** + * Updates or removes scan results for the corresponding StandardWifiEntries. + * New entries will be created for scan results without an existing entry. + * Unsaved and unreachable entries will be removed. + * + * @param scanResults List of valid scan results to convey as StandardWifiEntries + */ + @WorkerThread + 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::createStandardWifiEntryKey)); + .collect(groupingBy(StandardWifiEntry::scanResultToStandardWifiEntryKey)); + + // Iterate through current entries and update each entry's scan results + mStandardWifiEntryCache.entrySet().removeIf(e -> { + final String key = e.getKey(); + final StandardWifiEntry entry = e.getValue(); + // Update scan results if available, or set to null. + entry.updateScanResultInfo(scanResultsByKey.remove(key)); + // Entry is not saved and is unreachable, remove it. + return !entry.isSaved() && entry.getLevel() == WIFI_LEVEL_UNREACHABLE; + }); - // Create or get cached StandardWifiEntry by key - for (String key : scanResultsByKey.keySet()) { - StandardWifiEntry entry = mStandardWifiEntryCache.get(key); - if (entry == null) { - entry = new StandardWifiEntry(mMainHandler, scanResultsByKey.get(key)); - } else { - entry.updateScanResultInfo(scanResultsByKey.get(key)); - } - updatedStandardWifiEntries.put(key, entry); + // Create new StandardWifiEntry objects for each leftover group of scan results. + for (Map.Entry<String, List<ScanResult>> e: scanResultsByKey.entrySet()) { + mStandardWifiEntryCache.put(e.getKey(), + new StandardWifiEntry(mMainHandler, e.getValue())); } - mStandardWifiEntryCache.clear(); - mStandardWifiEntryCache.putAll(updatedStandardWifiEntries); } /** diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiTracker2Test.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiTracker2Test.java index 8cbb04dab..f7dc5cc30 100644 --- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiTracker2Test.java +++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiTracker2Test.java @@ -16,7 +16,6 @@ package com.android.wifitrackerlib; -import static com.android.wifitrackerlib.StandardWifiEntry.createStandardWifiEntryKey; import static com.android.wifitrackerlib.TestUtils.buildScanResult; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +31,7 @@ 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; @@ -47,6 +47,7 @@ import org.mockito.MockitoAnnotations; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class WifiTracker2Test { @@ -228,8 +229,8 @@ public class WifiTracker2Test { } assertThat(seenKeys).containsExactly( - createStandardWifiEntryKey(openNetwork), - createStandardWifiEntryKey(secureNetwork)); + StandardWifiEntry.scanResultToStandardWifiEntryKey(openNetwork), + StandardWifiEntry.scanResultToStandardWifiEntryKey(secureNetwork)); } /** @@ -307,4 +308,67 @@ public class WifiTracker2Test { // Successful scan should time out old entries. assertThat(wifiTracker2.getWifiEntries()).isEmpty(); } + + /** + * Tests that a CONFIGURED_NETWORKS_CHANGED broadcast updates the correct WifiEntry from + * unsaved to saved. + */ + @Test + public void testGetWifiEntries_configuredNetworksChanged_unsavedToSaved() { + final WifiTracker2 wifiTracker2 = createTestWifiTracker2(); + wifiTracker2.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + + when(mMockWifiManager.getScanResults()).thenReturn(Arrays.asList( + buildScanResult("ssid", "bssid", START_MILLIS))); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + final WifiEntry entry = wifiTracker2.getWifiEntries().get(0); + + assertThat(entry.isSaved()).isFalse(); + + 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(entry.isSaved()).isTrue(); + } + + /** + * Tests that a CONFIGURED_NETWORKS_CHANGED broadcast updates the correct WifiEntry from + * saved to unsaved. + */ + @Test + public void testGetWifiEntries_configuredNetworksChanged_savedToUnsaved() { + final WifiTracker2 wifiTracker2 = createTestWifiTracker2(); + final WifiConfiguration config = new WifiConfiguration(); + config.SSID = "\"ssid\""; + when(mMockWifiManager.getConfiguredNetworks()) + .thenReturn(Collections.singletonList(config)); + wifiTracker2.onStart(); + verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + any(), any(), any()); + mTestLooper.dispatchAll(); + + when(mMockWifiManager.getScanResults()).thenReturn(Arrays.asList( + buildScanResult("ssid", "bssid", START_MILLIS))); + mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, + new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + final WifiEntry entry = wifiTracker2.getWifiEntries().get(0); + + assertThat(entry.isSaved()).isTrue(); + + 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(entry.isSaved()).isFalse(); + } } |