From 46adcb39a183597343cc1184521c0684212e6c5e Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Mon, 7 Oct 2019 06:14:58 -0700 Subject: WifiNetworkFactory: Use the latest cached scan results Before starting scans for processing a network request, fetch the latest cached scan results to speed up the matching process. The cached scan results are filtered out to remove scan results that are older than 20 seconds. As long as there was a previous scan in the last 20 seconds, there are 2 benefits with this change: a) There is currently a delay of 2-3 seconds after the request where the user sees a blank screen when platform is performing the first scan. This can be avoided and we can present the user with matches immediately when the request is received. b) If the request is for a specific bssid and there is an approved match in the cached scan results, then we can bypass the UI and trigger connection. Also, renames "PreviouslyApproved" -> "Approved" in test name to ensure the test names fit in one line :) Bug: 134712530 Test: atest com.android.server.wifi Test: Manually verified with CtsVerifier tests that the UI does not start out with a blank page (because the matched networks are already found by using the cached results). Change-Id: I7a788777aabd11562077be88fcc4c9cd45e5b9ab Merged-In: I7a788777aabd11562077be88fcc4c9cd45e5b9ab (cherry-picked from 0ab35f26f0da8885a5d249a678187993da425447) --- .../android/server/wifi/WifiNetworkFactory.java | 113 ++++++++++++++------- 1 file changed, 78 insertions(+), 35 deletions(-) (limited to 'service') diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index 48797f773..be6ac6473 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -82,6 +82,8 @@ public class WifiNetworkFactory extends NetworkFactory { @VisibleForTesting private static final int SCORE_FILTER = 60; @VisibleForTesting + public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 20 * 1000; // 20 seconds + @VisibleForTesting public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds @VisibleForTesting public static final int NETWORK_CONNECTION_TIMEOUT_MS = 30 * 1000; // 30 seconds @@ -230,37 +232,10 @@ public class WifiNetworkFactory extends NetworkFactory { if (mVerboseLoggingEnabled) { Log.v(TAG, "Received " + scanResults.length + " scan results"); } - List matchedScanResults = - getNetworksMatchingActiveNetworkRequest(scanResults); - if (mActiveMatchedScanResults == null) { - // only note the first match size in metrics (chances of this changing in further - // scans is pretty low) - mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram( - matchedScanResults.size()); - } - mActiveMatchedScanResults = matchedScanResults; - - ScanResult approvedScanResult = null; - if (isActiveRequestForSingleAccessPoint()) { - approvedScanResult = - findUserApprovedAccessPointForActiveRequestFromActiveMatchedScanResults(); - } - if (approvedScanResult != null - && !mWifiConfigManager.wasEphemeralNetworkDeleted( - ScanResultUtil.createQuotedSSID(approvedScanResult.SSID))) { - Log.v(TAG, "Approved access point found in matching scan results. " - + "Triggering connect " + approvedScanResult); - handleConnectToNetworkUserSelectionInternal( - ScanResultUtil.createNetworkFromScanResult(approvedScanResult)); - mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass(); - // TODO (b/122658039): Post notification. - } else { - if (mVerboseLoggingEnabled) { - Log.v(TAG, "No approved access points found in matching scan results. " - + "Sending match callback"); - } - sendNetworkRequestMatchCallbacksForActiveRequest(matchedScanResults); - // Didn't find an approved match, schedule the next scan. + if (!handleScanResultsAndTriggerConnectIfUserApprovedMatchFound(scanResults)) { + // Didn't find an approved match, send the matching results to UI and schedule the + // next scan. + sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults); scheduleNextPeriodicScan(); } } @@ -612,10 +587,19 @@ public class WifiNetworkFactory extends NetworkFactory { wns.requestorUid, wns.requestorPackageName); mWifiMetrics.incrementNetworkRequestApiNumRequest(); - // Start UI to let the user grant/disallow this request from the app. - startUi(); - // Trigger periodic scans for finding a network in the request. - startPeriodicScans(); + // Fetch the latest cached scan results to speed up network matching. + ScanResult[] cachedScanResults = getFilteredCachedScanResults(); + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Using cached " + cachedScanResults.length + " scan results"); + } + if (!handleScanResultsAndTriggerConnectIfUserApprovedMatchFound(cachedScanResults)) { + // Start UI to let the user grant/disallow this request from the app. + startUi(); + // Didn't find an approved match, send the matching results to UI and trigger + // periodic scans for finding a network in the request. + sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults); + startPeriodicScans(); + } } } @@ -1330,6 +1314,65 @@ public class WifiNetworkFactory extends NetworkFactory { saveToStore(); } + /** + * Handle scan results + * a) Find all scan results matching the active network request. + * b) If the request is for a single bssid, check if the matching ScanResult was pre-approved + * by the user. + * c) If yes to (b), trigger a connect immediately and returns true. Else, returns false. + * + * @param scanResults Array of {@link ScanResult} to be processed. + * @return true if a pre-approved network was found for connection, false otherwise. + */ + private boolean handleScanResultsAndTriggerConnectIfUserApprovedMatchFound( + ScanResult[] scanResults) { + List matchedScanResults = + getNetworksMatchingActiveNetworkRequest(scanResults); + if ((mActiveMatchedScanResults == null || mActiveMatchedScanResults.isEmpty()) + && !matchedScanResults.isEmpty()) { + // only note the first match size in metrics (chances of this changing in further + // scans is pretty low) + mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram( + matchedScanResults.size()); + } + mActiveMatchedScanResults = matchedScanResults; + + ScanResult approvedScanResult = null; + if (isActiveRequestForSingleAccessPoint()) { + approvedScanResult = + findUserApprovedAccessPointForActiveRequestFromActiveMatchedScanResults(); + } + if (approvedScanResult != null + && !mWifiConfigManager.wasEphemeralNetworkDeleted( + ScanResultUtil.createQuotedSSID(approvedScanResult.SSID))) { + Log.v(TAG, "Approved access point found in matching scan results. " + + "Triggering connect " + approvedScanResult); + handleConnectToNetworkUserSelectionInternal( + ScanResultUtil.createNetworkFromScanResult(approvedScanResult)); + mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass(); + return true; + } + if (mVerboseLoggingEnabled) { + Log.v(TAG, "No approved access points found in matching scan results"); + } + return false; + } + + /** + * Retrieve the latest cached scan results from wifi scanner and filter out any + * {@link ScanResult} older than {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}. + */ + private @NonNull ScanResult[] getFilteredCachedScanResults() { + List cachedScanResults = mWifiScanner.getSingleScanResults(); + if (cachedScanResults == null || cachedScanResults.isEmpty()) return new ScanResult[0]; + long currentTimeInMillis = mClock.getElapsedSinceBootMillis(); + return cachedScanResults.stream() + .filter(scanResult + -> ((currentTimeInMillis - (scanResult.timestamp / 1000)) + < CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS)) + .toArray(ScanResult[]::new); + } + /** * Clean up least recently used Access Points if specified app reach the limit. */ -- cgit v1.2.3 From 6ac4bfcb70821f418cd748696b99912dae48f257 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Mon, 7 Oct 2019 07:24:11 -0700 Subject: WifiNetworkFactory: Send match callback on registration If the UI registers its callback to the platform and if we already have some matching results for the request, send it immediately to the UI. Otherwise, we'll need to wait for the next scan results to present users with matches. Bug: 134712530 Test: atest com.android.server.wifi Test: Manually verified with CtsVerifier tests that the UI does not start out with a blank page (because the matched networks are already found by using the cached results). Change-Id: Ice7f89952c0a71c181ea5de58e417c02e7f2bf91 Merged-In: Ice7f89952c0a71c181ea5de58e417c02e7f2bf91 (cherry-picked from bbaee0b899a9606f405e27e0af50ee98d2023de4) --- service/java/com/android/server/wifi/WifiNetworkFactory.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'service') diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index be6ac6473..4b5866b44 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -428,7 +428,12 @@ public class WifiNetworkFactory extends NetworkFactory { new NetworkFactoryUserSelectionCallback(mActiveSpecificNetworkRequest)); } catch (RemoteException e) { Log.e(TAG, "Unable to invoke user selection registration callback " + callback, e); + return; } + + // If we are already in the midst of processing a request, send matching callbacks + // immediately on registering the callback. + sendNetworkRequestMatchCallbacksForActiveRequest(mActiveMatchedScanResults); } /** @@ -981,6 +986,7 @@ public class WifiNetworkFactory extends NetworkFactory { mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier; mActiveSpecificNetworkRequest = null; mActiveSpecificNetworkRequestSpecifier = null; + mActiveMatchedScanResults = null; mPendingConnectionSuccess = false; // Cancel connection timeout alarm. cancelConnectionTimeout(); @@ -1127,7 +1133,8 @@ public class WifiNetworkFactory extends NetworkFactory { } private void sendNetworkRequestMatchCallbacksForActiveRequest( - List matchedScanResults) { + @Nullable List matchedScanResults) { + if (matchedScanResults == null || matchedScanResults.isEmpty()) return; if (mRegisteredCallbacks.getNumCallbacks() == 0) { Log.e(TAG, "No callback registered for sending network request matches. " + "Ignoring..."); -- cgit v1.2.3