diff options
author | Nate Jiang <qiangjiang@google.com> | 2019-12-10 02:34:07 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-12-10 02:34:07 +0000 |
commit | 47fc6c5f3eb6ba273c3f04890971a63c31324996 (patch) | |
tree | 57c9b66c1cf0f55d71e1145f3fe276dc7516f583 /service | |
parent | f05e2f3f34b31061c22e5ceaed861a4e9f97a69d (diff) | |
parent | 3fe95912af8f17c5964a607aa9133c3f87bbe920 (diff) |
Merge changes Ic33d2d48,Ic40a0e39,I96ef09f5,I996dd19a
* changes:
[passpoint] match the best scanDetail to the provider
allows multiple passpoint candidates
rename evaluator to nominator
[Evaluator] evaluator will only nominate candidates
Diffstat (limited to 'service')
17 files changed, 605 insertions, 791 deletions
diff --git a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java b/service/java/com/android/server/wifi/CarrierNetworkNominator.java index c9f567e89..c1a3e02a3 100644 --- a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/CarrierNetworkNominator.java @@ -23,7 +23,7 @@ import android.os.Process; import android.telephony.TelephonyManager; import android.util.LocalLog; -import com.android.server.wifi.WifiNetworkSelector.NetworkEvaluator; +import com.android.server.wifi.WifiNetworkSelector.NetworkNominator; import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.util.TelephonyUtil; @@ -32,21 +32,18 @@ import java.util.List; import javax.annotation.concurrent.NotThreadSafe; /** - * Evaluator to select a Carrier Wi-Fi network which can be connected to. The evaluator performs + * Nominator to select a Carrier Wi-Fi network which can be connected to. The Nominator performs * two functions: * * 1. Filtering: figure out which of the networks is a Carrier Wi-Fi network (using the * {@link CarrierNetworkConfig} APIs). - * 2. Evaluation: current evaluator API has 2 outputs (effectively): - * - Connectable networks: all networks which match #1 will be fed to this API - * - Selected network: a single network 'selected' by the evaluator. A simple max(RSSI) will be - * used to pick one network from among the connectable networks. + * 2. Nominating: Connectable networks: all networks which match #1 will be fed to this API * * Note: This class is not thread safe and meant to be used only from {@link WifiNetworkSelector}. */ @NotThreadSafe -public class CarrierNetworkEvaluator implements NetworkEvaluator { - private static final String TAG = "CarrierNetworkEvaluator"; +public class CarrierNetworkNominator implements NetworkNominator { + private static final String TAG = "CarrierNetworkNominator"; private final WifiConfigManager mWifiConfigManager; private final CarrierNetworkConfig mCarrierNetworkConfig; @@ -54,7 +51,7 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { private final WifiInjector mWifiInjector; private TelephonyManager mTelephonyManager; - public CarrierNetworkEvaluator(WifiConfigManager wifiConfigManager, + public CarrierNetworkNominator(WifiConfigManager wifiConfigManager, CarrierNetworkConfig carrierNetworkConfig, LocalLog localLog, WifiInjector wifiInjector) { mWifiConfigManager = wifiConfigManager; @@ -71,8 +68,8 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { } @Override - public @EvaluatorId int getId() { - return EVALUATOR_ID_CARRIER; + public @NominatorId int getId() { + return NOMINATOR_ID_CARRIER; } @Override @@ -86,15 +83,13 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { } @Override - public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + public void nominateNetworks(List<ScanDetail> scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, OnConnectableListener onConnectableListener) { if (!mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) { - return null; + return; } - int currentMaxRssi = Integer.MIN_VALUE; - WifiConfiguration configWithMaxRssi = null; for (ScanDetail scanDetail : scanDetails) { ScanResult scanResult = scanDetail.getScanResult(); @@ -168,13 +163,7 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { mWifiConfigManager.updateScanDetailForNetwork(result.getNetworkId(), scanDetail); } - onConnectableListener.onConnectable(scanDetail, config, 0); - if (scanResult.level > currentMaxRssi) { - configWithMaxRssi = config; - currentMaxRssi = scanResult.level; - } + onConnectableListener.onConnectable(scanDetail, config); } - - return configWithMaxRssi; } } diff --git a/service/java/com/android/server/wifi/CompatibilityScorer.java b/service/java/com/android/server/wifi/CompatibilityScorer.java index e354b70a8..f2ec5b9df 100644 --- a/service/java/com/android/server/wifi/CompatibilityScorer.java +++ b/service/java/com/android/server/wifi/CompatibilityScorer.java @@ -98,7 +98,7 @@ final class CompatibilityScorer implements WifiCandidates.CandidateScorer { // To simulate the old strict priority rule, subtract a penalty based on // which evaluator added the candidate. - score -= 1000 * candidate.getEvaluatorId(); + score -= 1000 * candidate.getNominatorId(); // The old method breaks ties on the basis of RSSI, which we can // emulate easily since our score does not need to be an integer. diff --git a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java index c8f370bcc..08bfb65a4 100644 --- a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java +++ b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java @@ -28,7 +28,6 @@ import com.android.server.wifi.util.ScanResultUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,30 +38,21 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; /** - * Evaluator to pick the best network to connect to from the list of active network suggestions - * provided by apps. + * Nominator nominate the highest available suggestion candidates. * Note: * <li> This class is not thread safe and meant to be used only from {@link WifiNetworkSelector}. * </li> * - * This is a non-optimal implementation which picks any network suggestion which matches - * the scan result with the highest RSSI. - * TODO: More advanced implementation will follow! - * Params to consider for evaluating network suggestions: - * - Regular network evaluator params like security, band, RSSI, etc. - * - Priority of suggestions provided by a single app. - * - Whether the network suggestions requires user/app interaction or if it is metered. - * - Historical quality of suggestions provided by the corresponding app. */ @NotThreadSafe -public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEvaluator { - private static final String TAG = "NetworkSuggestionEvaluator"; +public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNominator { + private static final String TAG = "NetworkSuggestionNominator"; private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; private final WifiConfigManager mWifiConfigManager; private final LocalLog mLocalLog; - NetworkSuggestionEvaluator(WifiNetworkSuggestionsManager networkSuggestionsManager, + NetworkSuggestionNominator(WifiNetworkSuggestionsManager networkSuggestionsManager, WifiConfigManager wifiConfigManager, LocalLog localLog) { mWifiNetworkSuggestionsManager = networkSuggestionsManager; mWifiConfigManager = wifiConfigManager; @@ -75,7 +65,7 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv } @Override - public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + public void nominateNetworks(List<ScanDetail> scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, @NonNull OnConnectableListener onConnectableListener) { @@ -133,17 +123,9 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv // Return early on no match. if (matchMetaInfo.isEmpty()) { mLocalLog.log("did not see any matching network suggestions."); - return null; - } - // Note: These matched sets should be very small & hence these additional manipulations that - // follow should not be very expensive. - PerNetworkSuggestionMatchMetaInfo candidate = - matchMetaInfo.findConnectableNetworksAndPickBest(onConnectableListener); - if (candidate == null) { // should never happen. - Log.wtf(TAG, "Unexepectedly got null"); - return null; + return; } - return candidate.wCmConfiguredNetwork; + matchMetaInfo.findConnectableNetworksAndHighestPriority(onConnectableListener); } // Add and enable this network to the central database (i.e WifiConfigManager). @@ -170,8 +152,8 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv } @Override - public @EvaluatorId int getId() { - return EVALUATOR_ID_SUGGESTION; + public @NominatorId int getId() { + return NOMINATOR_ID_SUGGESTION; } @Override @@ -263,16 +245,11 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv } /** - * Find all the connectable networks and pick the best network among the current match info - * candidates. - * - * Among the highest priority suggestions from different packages, choose the suggestion - * with the highest RSSI. - * Note: This should need to be replaced by a more sophisticated algorithm. + * Run through all connectable suggestions and nominate highest priority networks from each + * app as candidates to {@link WifiNetworkSelector}. */ - public PerNetworkSuggestionMatchMetaInfo findConnectableNetworksAndPickBest( + public void findConnectableNetworksAndHighestPriority( @NonNull OnConnectableListener onConnectableListener) { - List<PerNetworkSuggestionMatchMetaInfo> allMatchedNetworkInfos = new ArrayList<>(); for (PerAppMatchMetaInfo appInfo : mAppInfos.values()) { List<PerNetworkSuggestionMatchMetaInfo> matchedNetworkInfos = appInfo.getHighestPriorityNetworks(); @@ -292,23 +269,11 @@ public class NetworkSuggestionEvaluator implements WifiNetworkSelector.NetworkEv WifiNetworkSelector.toNetworkString( matchedNetworkInfo.wCmConfiguredNetwork))); } - allMatchedNetworkInfos.add(matchedNetworkInfo); - // Invoke onConnectable for the best networks from each app. onConnectableListener.onConnectable( matchedNetworkInfo.matchingScanDetail, - matchedNetworkInfo.wCmConfiguredNetwork, - 0); + matchedNetworkInfo.wCmConfiguredNetwork); } } - PerNetworkSuggestionMatchMetaInfo networkInfo = allMatchedNetworkInfos - .stream() - .max(Comparator.comparing(e -> e.matchingScanDetail.getScanResult().level)) - .orElse(null); - if (networkInfo == null) { // should never happen. - Log.wtf(TAG, "Unexepectedly got null"); - return null; - } - return networkInfo; } } diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java deleted file mode 100644 index d3254790b..000000000 --- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2016 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.server.wifi; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiConfiguration; -import android.util.LocalLog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.wifi.util.TelephonyUtil; -import com.android.wifi.resources.R; - -import java.util.List; - -/** - * This class is the WifiNetworkSelector.NetworkEvaluator implementation for - * saved networks. - */ -public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { - private static final String NAME = "SavedNetworkEvaluator"; - private final WifiConfigManager mWifiConfigManager; - private final Context mContext; - private final Clock mClock; - private final LocalLog mLocalLog; - private final WifiConnectivityHelper mConnectivityHelper; - private final TelephonyUtil mTelephonyUtil; - private final ScoringParams mScoringParams; - - /** - * Time it takes for the lastSelectionAward to decay by one point, in milliseconds - */ - @VisibleForTesting - public static final int LAST_SELECTION_AWARD_DECAY_MSEC = 60 * 1000; - - - SavedNetworkEvaluator(final Context context, ScoringParams scoringParams, - WifiConfigManager configManager, Clock clock, - LocalLog localLog, WifiConnectivityHelper connectivityHelper, - TelephonyUtil telephonyUtil) { - mContext = context; - mScoringParams = scoringParams; - mWifiConfigManager = configManager; - mClock = clock; - mLocalLog = localLog; - mConnectivityHelper = connectivityHelper; - mTelephonyUtil = telephonyUtil; - - } - - private void localLog(String log) { - mLocalLog.log(log); - } - - /** - * Get the evaluator type. - */ - @Override - public @EvaluatorId int getId() { - return EVALUATOR_ID_SAVED; - } - - /** - * Get the evaluator name. - */ - @Override - public String getName() { - return NAME; - } - - /** - * Update the evaluator. - */ - @Override - public void update(List<ScanDetail> scanDetails) { } - - private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network, - WifiConfiguration currentNetwork, String currentBssid, - StringBuffer sbuf) { - int score = 0; - boolean is5GHz = scanResult.is5GHz(); - boolean is6GHz = scanResult.is6GHz(); - - final int rssiScoreSlope = mContext.getResources().getInteger( - R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); - final int rssiScoreOffset = mContext.getResources().getInteger( - R.integer.config_wifi_framework_RSSI_SCORE_OFFSET); - final int sameBssidAward = mContext.getResources().getInteger( - R.integer.config_wifi_framework_SAME_BSSID_AWARD); - final int sameNetworkAward = mContext.getResources().getInteger( - R.integer.config_wifi_framework_current_network_boost); - final int lastSelectionAward = mContext.getResources().getInteger( - R.integer.config_wifi_framework_LAST_SELECTION_AWARD); - final int securityAward = mContext.getResources().getInteger( - R.integer.config_wifi_framework_SECURITY_AWARD); - final int band5GHzAward = mContext.getResources().getInteger( - R.integer.config_wifi_framework_5GHz_preference_boost_factor); - final int band6GHzAward = mContext.getResources().getInteger( - R.integer.config_wifiFramework6ghzPreferenceBoostFactor); - - sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID) - .append(" RSSI:").append(scanResult.level).append(" ] "); - // Calculate the RSSI score. - int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency); - int rssi = Math.min(scanResult.level, rssiSaturationThreshold); - score += (rssi + rssiScoreOffset) * rssiScoreSlope; - sbuf.append(" RSSI score: ").append(score).append(","); - - // 5GHz band bonus. - if (is5GHz) { - score += band5GHzAward; - sbuf.append(" 5GHz bonus: ").append(band5GHzAward).append(","); - } else if (is6GHz) { - score += band6GHzAward; - sbuf.append(" 6GHz bonus: ").append(band6GHzAward).append(","); - } - - // Last user selection award. - int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork(); - if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID - && lastUserSelectedNetworkId == network.networkId) { - long timeDifference = mClock.getElapsedSinceBootMillis() - - mWifiConfigManager.getLastSelectedTimeStamp(); - if (timeDifference > 0) { - int decay = (int) (timeDifference / LAST_SELECTION_AWARD_DECAY_MSEC); - int bonus = Math.max(lastSelectionAward - decay, 0); - score += bonus; - sbuf.append(" User selection ").append(timeDifference) - .append(" ms ago, bonus: ").append(bonus).append(","); - } - } - - // Same network award. - if (currentNetwork != null && network.networkId == currentNetwork.networkId) { - score += sameNetworkAward; - sbuf.append(" Same network bonus: ").append(sameNetworkAward).append(","); - - // When firmware roaming is supported, equivalent BSSIDs (the ones under the - // same network as the currently connected one) get the same BSSID award. - if (mConnectivityHelper.isFirmwareRoamingSupported() - && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) { - score += sameBssidAward; - sbuf.append(" Equivalent BSSID bonus: ").append(sameBssidAward).append(","); - } - } - - // Same BSSID award. - if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) { - score += sameBssidAward; - sbuf.append(" Same BSSID bonus: ").append(sameBssidAward).append(","); - } - - // Security award. - if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) { - score += securityAward; - sbuf.append(" Secure network bonus: ").append(securityAward).append(","); - } - - sbuf.append(" ## Total score: ").append(score).append("\n"); - - return score; - } - - /** - * Evaluate all the networks from the scan results and return - * the WifiConfiguration of the network chosen for connection. - * - * @return configuration of the chosen network; - * null if no network in this category is available. - */ - @Override - public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, - WifiConfiguration currentNetwork, String currentBssid, boolean connected, - boolean untrustedNetworkAllowed, - @NonNull OnConnectableListener onConnectableListener) { - int highestScore = Integer.MIN_VALUE; - ScanResult scanResultCandidate = null; - WifiConfiguration candidate = null; - StringBuffer scoreHistory = new StringBuffer(); - - for (ScanDetail scanDetail : scanDetails) { - ScanResult scanResult = scanDetail.getScanResult(); - - // One ScanResult can be associated with more than one network, hence we calculate all - // the scores and use the highest one as the ScanResult's score. - // TODO(b/112196799): this has side effects, rather not do that in an evaluator - WifiConfiguration network = - mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail); - - if (network == null) { - continue; - } - - // Ignore networks that the user has disallowed auto-join for. - if (!network.allowAutojoin) { - continue; - } - - /** - * Ignore Passpoint and Ephemeral networks. They are configured networks, - * but without being persisted to the storage. They are evaluated by - * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator} - * respectively. - */ - if (network.isPasspoint() || network.isEphemeral()) { - continue; - } - - WifiConfiguration.NetworkSelectionStatus status = - network.getNetworkSelectionStatus(); - // TODO (b/112196799): another side effect - status.setSeenInLastQualifiedNetworkSelection(true); - - if (!status.isNetworkEnabled()) { - continue; - } else if (network.BSSID != null && !network.BSSID.equals("any") - && !network.BSSID.equals(scanResult.BSSID)) { - // App has specified the only BSSID to connect for this - // configuration. So only the matching ScanResult can be a candidate. - localLog("Network " + WifiNetworkSelector.toNetworkString(network) - + " has specified BSSID " + network.BSSID + ". Skip " - + scanResult.BSSID); - continue; - } else if (network.enterpriseConfig != null - && network.enterpriseConfig.requireSimCredential()) { - int subId = mTelephonyUtil.getBestMatchSubscriptionId(network); - if (!mTelephonyUtil.isSimPresent(subId)) { - // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present. - localLog("No SIM card is good for Network " - + WifiNetworkSelector.toNetworkString(network)); - continue; - } - } - - int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid, - scoreHistory); - - // Set candidate ScanResult for all saved networks to ensure that users can - // override network selection. See WifiNetworkSelector#setUserConnectChoice. - if (score > status.getCandidateScore() - || (score == status.getCandidateScore() - && status.getCandidate() != null - && scanResult.level > status.getCandidate().level)) { - mWifiConfigManager.setNetworkCandidateScanResult( - network.networkId, scanResult, score); - } - - // If the network is marked to use external scores, or is an open network with - // curate saved open networks enabled, do not consider it for network selection. - if (network.useExternalScores) { - localLog("Network " + WifiNetworkSelector.toNetworkString(network) - + " has external score."); - continue; - } - - onConnectableListener.onConnectable(scanDetail, - mWifiConfigManager.getConfiguredNetwork(network.networkId), score); - - // TODO(b/112196799) - pull into common code - if (score > highestScore - || (score == highestScore - && scanResultCandidate != null - && scanResult.level > scanResultCandidate.level)) { - highestScore = score; - scanResultCandidate = scanResult; - mWifiConfigManager.setNetworkCandidateScanResult( - network.networkId, scanResultCandidate, highestScore); - // Reload the network config with the updated info. - candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId); - } - } - - if (scoreHistory.length() > 0) { - localLog("\n" + scoreHistory.toString()); - } - - if (scanResultCandidate == null) { - localLog("did not see any good candidates."); - } - return candidate; - } -} diff --git a/service/java/com/android/server/wifi/SavedNetworkNominator.java b/service/java/com/android/server/wifi/SavedNetworkNominator.java new file mode 100644 index 000000000..7c00ce7e1 --- /dev/null +++ b/service/java/com/android/server/wifi/SavedNetworkNominator.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 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.server.wifi; + +import android.annotation.NonNull; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.util.LocalLog; + +import com.android.server.wifi.util.TelephonyUtil; + +import java.util.List; + +/** + * This class is the WifiNetworkSelector.NetworkNominator implementation for + * saved networks. + */ +public class SavedNetworkNominator implements WifiNetworkSelector.NetworkNominator { + private static final String NAME = "SavedNetworkNominator"; + private final WifiConfigManager mWifiConfigManager; + private final LocalLog mLocalLog; + private final TelephonyUtil mTelephonyUtil; + + SavedNetworkNominator(WifiConfigManager configManager, LocalLog localLog, + TelephonyUtil telephonyUtil) { + mWifiConfigManager = configManager; + mLocalLog = localLog; + mTelephonyUtil = telephonyUtil; + } + + private void localLog(String log) { + mLocalLog.log(log); + } + + /** + * Get the Nominator type. + */ + @Override + public @NominatorId int getId() { + return NOMINATOR_ID_SAVED; + } + + /** + * Get the Nominator name. + */ + @Override + public String getName() { + return NAME; + } + + /** + * Update the Nominator. + */ + @Override + public void update(List<ScanDetail> scanDetails) { } + + /** + * Run through all scanDetails and nominate all connectable network as candidates. + * + */ + @Override + public void nominateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, boolean connected, + boolean untrustedNetworkAllowed, + @NonNull OnConnectableListener onConnectableListener) { + + for (ScanDetail scanDetail : scanDetails) { + ScanResult scanResult = scanDetail.getScanResult(); + + // One ScanResult can be associated with more than one network, hence we calculate all + // the scores and use the highest one as the ScanResult's score. + // TODO(b/112196799): this has side effects, rather not do that in a nominator + WifiConfiguration network = + mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail); + + if (network == null) { + continue; + } + + // Ignore networks that the user has disallowed auto-join for. + if (!network.allowAutojoin) { + continue; + } + + /** + * Ignore Passpoint and Ephemeral networks. They are configured networks, + * but without being persisted to the storage. They are nominated by + * {@link PasspointNetworkNominator} and {@link ScoredNetworkNominator} + * respectively. + */ + if (network.isPasspoint() || network.isEphemeral()) { + continue; + } + + WifiConfiguration.NetworkSelectionStatus status = + network.getNetworkSelectionStatus(); + // TODO (b/112196799): another side effect + status.setSeenInLastQualifiedNetworkSelection(true); + + if (!status.isNetworkEnabled()) { + continue; + } else if (network.BSSID != null && !network.BSSID.equals("any") + && !network.BSSID.equals(scanResult.BSSID)) { + // App has specified the only BSSID to connect for this + // configuration. So only the matching ScanResult can be a candidate. + localLog("Network " + WifiNetworkSelector.toNetworkString(network) + + " has specified BSSID " + network.BSSID + ". Skip " + + scanResult.BSSID); + continue; + } else if (network.enterpriseConfig != null + && network.enterpriseConfig.requireSimCredential()) { + int subId = mTelephonyUtil.getBestMatchSubscriptionId(network); + if (!mTelephonyUtil.isSimPresent(subId)) { + // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present. + localLog("No SIM card is good for Network " + + WifiNetworkSelector.toNetworkString(network)); + continue; + } + } + + // If the network is marked to use external scores, or is an open network with + // curate saved open networks enabled, do not consider it for network selection. + if (network.useExternalScores) { + localLog("Network " + WifiNetworkSelector.toNetworkString(network) + + " has external score."); + continue; + } + + onConnectableListener.onConnectable(scanDetail, + mWifiConfigManager.getConfiguredNetwork(network.networkId)); + } + } +} diff --git a/service/java/com/android/server/wifi/ScoreCardBasedScorer.java b/service/java/com/android/server/wifi/ScoreCardBasedScorer.java index 8e0c3e778..e8617e11b 100644 --- a/service/java/com/android/server/wifi/ScoreCardBasedScorer.java +++ b/service/java/com/android/server/wifi/ScoreCardBasedScorer.java @@ -106,7 +106,7 @@ final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { // To simulate the old strict priority rule, subtract a penalty based on // which evaluator added the candidate. - score -= 1000 * candidate.getEvaluatorId(); + score -= 1000 * candidate.getNominatorId(); return new ScoredCandidate(score, 10, USE_USER_CONNECT_CHOICE, candidate); diff --git a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java b/service/java/com/android/server/wifi/ScoredNetworkNominator.java index 000d1f69e..64df5ced7 100644 --- a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/ScoredNetworkNominator.java @@ -39,11 +39,11 @@ import java.util.ArrayList; import java.util.List; /** - * {@link WifiNetworkSelector.NetworkEvaluator} implementation that uses scores obtained by + * {@link WifiNetworkSelector.NetworkNominator} implementation that uses scores obtained by * {@link NetworkScoreManager#requestScores(NetworkKey[])} to make network connection decisions. */ -public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { - private static final String TAG = "ScoredNetworkEvaluator"; +public class ScoredNetworkNominator implements WifiNetworkSelector.NetworkNominator { + private static final String TAG = "ScoredNetworkNominator"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final NetworkScoreManager mNetworkScoreManager; @@ -55,7 +55,7 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua private boolean mNetworkRecommendationsEnabled; private WifiNetworkScoreCache mScoreCache; - ScoredNetworkEvaluator(final Context context, Handler handler, + ScoredNetworkNominator(final Context context, Handler handler, final FrameworkFacade frameworkFacade, NetworkScoreManager networkScoreManager, PackageManager packageManager, WifiConfigManager wifiConfigManager, LocalLog localLog, @@ -78,7 +78,7 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua Settings.Global.getUriFor(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), false /* notifyForDescendents */, mContentObserver); mContentObserver.onChange(false /* unused */); - mLocalLog.log("ScoredNetworkEvaluator constructed. mNetworkRecommendationsEnabled: " + mLocalLog.log("ScoredNetworkNominator constructed. mNetworkRecommendationsEnabled: " + mNetworkRecommendationsEnabled); } @@ -131,13 +131,13 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua } @Override - public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + public void nominateNetworks(List<ScanDetail> scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, @NonNull OnConnectableListener onConnectableListener) { if (!mNetworkRecommendationsEnabled) { - mLocalLog.log("Skipping evaluateNetworks; Network recommendations disabled."); - return null; + mLocalLog.log("Skipping nominateNetworks; Network recommendations disabled."); + return; } final ScoreTracker scoreTracker = new ScoreTracker(); @@ -188,11 +188,9 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua scoreTracker.trackExternallyScoredCandidate( scanResult, configuredNetwork, isCurrentNetwork); } - onConnectableListener.onConnectable(scanDetail, configuredNetwork, 0); + onConnectableListener.onConnectable(scanDetail, configuredNetwork); } - - - return scoreTracker.getCandidateConfiguration(onConnectableListener); + scoreTracker.getCandidateConfiguration(onConnectableListener); } /** Used to track the network with the highest score. */ @@ -340,14 +338,14 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua break; case ScoreTracker.EXTERNAL_SCORED_NONE: default: - mLocalLog.log("ScoredNetworkEvaluator did not see any good candidates."); + mLocalLog.log("ScoredNetworkNominator did not see any good candidates."); break; } WifiConfiguration ans = mWifiConfigManager.getConfiguredNetwork( candidateNetworkId); if (ans != null && mScanDetailCandidate != null) { // This is a newly created config, so we need to call onConnectable. - onConnectableListener.onConnectable(mScanDetailCandidate, ans, 0); + onConnectableListener.onConnectable(mScanDetailCandidate, ans); } return ans; } @@ -360,8 +358,8 @@ public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvalua } @Override - public @EvaluatorId int getId() { - return EVALUATOR_ID_SCORED; + public @NominatorId int getId() { + return NOMINATOR_ID_SCORED; } @Override diff --git a/service/java/com/android/server/wifi/ThroughputScorer.java b/service/java/com/android/server/wifi/ThroughputScorer.java index 1e42657c6..8177462de 100644 --- a/service/java/com/android/server/wifi/ThroughputScorer.java +++ b/service/java/com/android/server/wifi/ThroughputScorer.java @@ -97,7 +97,7 @@ final class ThroughputScorer implements WifiCandidates.CandidateScorer { // To simulate the old strict priority rule, subtract a penalty based on // which evaluator added the candidate. - int evaluatorGroupScore = -1000 * candidate.getEvaluatorId(); + int evaluatorGroupScore = -1000 * candidate.getNominatorId(); int score = rssiBaseScore + throughputBonusScore + lastSelectionBonusScore + currentNetworkBoost + securityAward + evaluatorGroupScore; diff --git a/service/java/com/android/server/wifi/UntrustedWifiNetworkFactory.java b/service/java/com/android/server/wifi/UntrustedWifiNetworkFactory.java index 7eae75827..f3dd6000c 100644 --- a/service/java/com/android/server/wifi/UntrustedWifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/UntrustedWifiNetworkFactory.java @@ -27,7 +27,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; /** - * Network factory to handle untrusted (used for {@link ScoredNetworkEvaluator}) wifi network + * Network factory to handle untrusted (used for {@link ScoredNetworkNominator}) wifi network * requests. */ public class UntrustedWifiNetworkFactory extends NetworkFactory { diff --git a/service/java/com/android/server/wifi/WifiCandidates.java b/service/java/com/android/server/wifi/WifiCandidates.java index d0f354232..1f6ed8dee 100644 --- a/service/java/com/android/server/wifi/WifiCandidates.java +++ b/service/java/com/android/server/wifi/WifiCandidates.java @@ -84,14 +84,15 @@ public class WifiCandidates { /** * Returns the ID of the evaluator that provided the candidate. */ - @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int getEvaluatorId(); + @WifiNetworkSelector.NetworkNominator.NominatorId + int getNominatorId(); /** * Gets the score that was provided by the evaluator. * * Not all evaluators provide a useful score. Scores from different evaluators * are not directly comparable. */ - int getEvaluatorScore(); + int getNominatorScore(); /** * Returns true if the candidate is in the same network as the * current connection. @@ -134,7 +135,7 @@ public class WifiCandidates { public final ScanDetail scanDetail; public final WifiConfiguration config; // First evaluator to nominate this config - public final @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId; + public final @WifiNetworkSelector.NetworkNominator.NominatorId int evaluatorId; public final int evaluatorScore; // Score provided by first nominating evaluator public final double lastSelectionWeight; // Value between 0 and 1 @@ -147,7 +148,7 @@ public class WifiCandidates { CandidateImpl(Key key, ScanDetail scanDetail, WifiConfiguration config, - @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId, + @WifiNetworkSelector.NetworkNominator.NominatorId int evaluatorId, int evaluatorScore, WifiScoreCard.PerBssid perBssid, double lastSelectionWeight, @@ -210,12 +211,12 @@ public class WifiCandidates { } @Override - public @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int getEvaluatorId() { + public @WifiNetworkSelector.NetworkNominator.NominatorId int getNominatorId() { return evaluatorId; } @Override - public int getEvaluatorScore() { + public int getNominatorScore() { return evaluatorScore; } @@ -368,7 +369,7 @@ public class WifiCandidates { */ public boolean add(ScanDetail scanDetail, WifiConfiguration config, - @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId, + @WifiNetworkSelector.NetworkNominator.NominatorId int evaluatorId, int evaluatorScore, double lastSelectionWeightBetweenZeroAndOne, boolean isMetered, @@ -383,9 +384,13 @@ public class WifiCandidates { } catch (RuntimeException e) { return failWithException(e); } - ScanResultMatchInfo key1 = ScanResultMatchInfo.fromWifiConfiguration(config); - ScanResultMatchInfo key2 = ScanResultMatchInfo.fromScanResult(scanResult); - if (!key1.equals(key2)) return failure(key1, key2); + ScanResultMatchInfo key1 = ScanResultMatchInfo.fromScanResult(scanResult); + if (!config.isPasspoint()) { + ScanResultMatchInfo key2 = ScanResultMatchInfo.fromWifiConfiguration(config); + if (!key1.equals(key2)) { + return failure(key1, key2); + } + } Key key = new Key(key1, bssid, config.networkId); CandidateImpl old = mCandidates.get(key); if (old != null) { diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 44711096d..c00c33019 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -53,6 +53,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.hotspot2.PasspointManager; +import com.android.server.wifi.hotspot2.PasspointNetworkNominator; import com.android.server.wifi.util.TelephonyUtil; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; @@ -939,7 +940,7 @@ public class WifiConfigManager { } // Passpoint configurations are generated and managed by PasspointManager. They can be - // added by either PasspointNetworkEvaluator (for auto connection) or Settings app + // added by either PasspointNetworkNominator (for auto connection) or Settings app // (for manual connection), and need to be removed once the connection is completed. // Since it is "owned" by us, so always allow us to modify them. if (config.isPasspoint() && uid == Process.WIFI_UID) { @@ -2461,7 +2462,7 @@ public class WifiConfigManager { /** * Save the ScanDetail to the ScanDetailCache of the given network. This is used - * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching + * by {@link PasspointNetworkNominator} for caching * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network. * * @param networkId The ID of the network to save ScanDetail to diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 9e2fa6e2d..214e4a223 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -49,7 +49,7 @@ import android.util.Log; import com.android.server.wifi.aware.WifiAwareMetrics; import com.android.server.wifi.hotspot2.PasspointManager; -import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator; +import com.android.server.wifi.hotspot2.PasspointNetworkNominator; import com.android.server.wifi.hotspot2.PasspointObjectFactory; import com.android.server.wifi.p2p.SupplicantP2pIfaceHal; import com.android.server.wifi.p2p.WifiP2pMetrics; @@ -124,11 +124,11 @@ public class WifiInjector { private final WifiConnectivityHelper mWifiConnectivityHelper; private final LocalLog mConnectivityLocalLog; private final WifiNetworkSelector mWifiNetworkSelector; - private final SavedNetworkEvaluator mSavedNetworkEvaluator; - private final NetworkSuggestionEvaluator mNetworkSuggestionEvaluator; - private final PasspointNetworkEvaluator mPasspointNetworkEvaluator; - private final ScoredNetworkEvaluator mScoredNetworkEvaluator; - private final CarrierNetworkEvaluator mCarrierNetworkEvaluator; + private final SavedNetworkNominator mSavedNetworkNominator; + private final NetworkSuggestionNominator mNetworkSuggestionNominator; + private final PasspointNetworkNominator mPasspointNetworkNominator; + private final ScoredNetworkNominator mScoredNetworkNominator; + private final CarrierNetworkNominator mCarrierNetworkNominator; private final WifiNetworkScoreCache mWifiNetworkScoreCache; private final NetworkScoreManager mNetworkScoreManager; private WifiScanner mWifiScanner; @@ -292,25 +292,23 @@ public class WifiInjector { ThroughputScorer throughputScorer = new ThroughputScorer(mScoringParams); mWifiNetworkSelector.registerCandidateScorer(throughputScorer); mWifiMetrics.setWifiNetworkSelector(mWifiNetworkSelector); - mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext, mScoringParams, - mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiConnectivityHelper, - mTelephonyUtil); + mSavedNetworkNominator = new SavedNetworkNominator( + mWifiConfigManager, mConnectivityLocalLog, mTelephonyUtil); mWifiNetworkSuggestionsManager = new WifiNetworkSuggestionsManager(mContext, wifiHandler, this, mWifiPermissionsUtil, mWifiConfigManager, mWifiConfigStore, mWifiMetrics, mTelephonyUtil); - mNetworkSuggestionEvaluator = new NetworkSuggestionEvaluator(mWifiNetworkSuggestionsManager, + mNetworkSuggestionNominator = new NetworkSuggestionNominator(mWifiNetworkSuggestionsManager, mWifiConfigManager, mConnectivityLocalLog); - mScoredNetworkEvaluator = new ScoredNetworkEvaluator(mContext, wifiHandler, + mScoredNetworkNominator = new ScoredNetworkNominator(mContext, wifiHandler, mFrameworkFacade, mNetworkScoreManager, mContext.getPackageManager(), mWifiConfigManager, mConnectivityLocalLog, mWifiNetworkScoreCache, mWifiPermissionsUtil); - mCarrierNetworkEvaluator = new CarrierNetworkEvaluator(mWifiConfigManager, + mCarrierNetworkNominator = new CarrierNetworkNominator(mWifiConfigManager, mCarrierNetworkConfig, mConnectivityLocalLog, this); mPasspointManager = new PasspointManager(mContext, this, - wifiHandler, mWifiNative, mWifiKeyStore, mClock, - new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore, - mWifiMetrics, mTelephonyUtil); - mPasspointNetworkEvaluator = new PasspointNetworkEvaluator( + wifiHandler, mWifiNative, mWifiKeyStore, mClock, new PasspointObjectFactory(), + mWifiConfigManager, mWifiConfigStore, mWifiMetrics, mTelephonyUtil); + mPasspointNetworkNominator = new PasspointNetworkNominator( mPasspointManager, mWifiConfigManager, mConnectivityLocalLog, this, subscriptionManager); mWifiMetrics.setPasspointManager(mPasspointManager); @@ -358,12 +356,12 @@ public class WifiInjector { mDppManager = new DppManager(wifiHandler, mWifiNative, mWifiConfigManager, mContext, mDppMetrics); - // Register the various network evaluators with the network selector. - mWifiNetworkSelector.registerNetworkEvaluator(mSavedNetworkEvaluator); - mWifiNetworkSelector.registerNetworkEvaluator(mNetworkSuggestionEvaluator); - mWifiNetworkSelector.registerNetworkEvaluator(mPasspointNetworkEvaluator); - mWifiNetworkSelector.registerNetworkEvaluator(mCarrierNetworkEvaluator); - mWifiNetworkSelector.registerNetworkEvaluator(mScoredNetworkEvaluator); + // Register the various network Nominators with the network selector. + mWifiNetworkSelector.registerNetworkNominator(mSavedNetworkNominator); + mWifiNetworkSelector.registerNetworkNominator(mNetworkSuggestionNominator); + mWifiNetworkSelector.registerNetworkNominator(mPasspointNetworkNominator); + mWifiNetworkSelector.registerNetworkNominator(mCarrierNetworkNominator); + mWifiNetworkSelector.registerNetworkNominator(mScoredNetworkNominator); mClientModeImpl.start(); } diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index a94521aea..e8d62a56d 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -2139,13 +2139,9 @@ public class WifiMetrics { } ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromScanResult(scanResult); - Pair<PasspointProvider, PasspointMatch> providerMatch = null; - PasspointProvider passpointProvider = null; + List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = null; if (networkDetail.isInterworking()) { - providerMatch = - mPasspointManager.matchProvider(scanResult); - passpointProvider = providerMatch != null ? providerMatch.first : null; - + matchedProviders = mPasspointManager.matchProvider(scanResult); if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) { passpointR1Aps++; } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) { @@ -2190,7 +2186,6 @@ public class WifiMetrics { mWifiConfigManager.getConfiguredNetworkForScanDetail(scanDetail); boolean isSaved = (config != null) && !config.isEphemeral() && !config.isPasspoint(); - boolean isSavedPasspoint = passpointProvider != null; if (isOpen) { openSsids.add(matchInfo); openBssids++; @@ -2203,8 +2198,11 @@ public class WifiMetrics { openOrSavedBssids++; // Calculate openOrSavedSsids union later } - if (isSavedPasspoint) { - savedPasspointProviderProfiles.add(passpointProvider); + if (matchedProviders != null && !matchedProviders.isEmpty()) { + for (Pair<PasspointProvider, PasspointMatch> passpointProvider : + matchedProviders) { + savedPasspointProviderProfiles.add(passpointProvider.first); + } savedPasspointProviderBssids++; } } diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index 433fd0b01..e794ef7bc 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -29,7 +29,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; import android.util.Pair; @@ -125,51 +124,49 @@ public class WifiNetworkSelector { /** * WiFi Network Selector supports various categories of networks. Each category - * has an evaluator to choose the best WiFi network to connect to. Evaluators - * should be registered in order, by decreasing importance. + * has an nominator to choose the best WiFi network to connect to. * Wifi Network Selector iterates through the registered scorers in registration order * before making a final selection from among the candidates. */ /** - * Interface for WiFi Network Evaluator + * Interface for WiFi Network Nominator * - * A network evaluator examines the scan results and recommends the - * best network in its category to connect to; it also reports the + * A network nominator examines the scan results reports the * connectable candidates in its category for further consideration. */ - public interface NetworkEvaluator { - /** Type of evaluators */ - int EVALUATOR_ID_SAVED = 0; - int EVALUATOR_ID_SUGGESTION = 1; - int EVALUATOR_ID_PASSPOINT = 2; - int EVALUATOR_ID_CARRIER = 3; - int EVALUATOR_ID_SCORED = 4; - - @IntDef(prefix = { "EVALUATOR_ID_" }, value = { - EVALUATOR_ID_SAVED, - EVALUATOR_ID_SUGGESTION, - EVALUATOR_ID_PASSPOINT, - EVALUATOR_ID_CARRIER, - EVALUATOR_ID_SCORED}) + public interface NetworkNominator { + /** Type of nominators */ + int NOMINATOR_ID_SAVED = 0; + int NOMINATOR_ID_SUGGESTION = 1; + int NOMINATOR_ID_PASSPOINT = 2; + int NOMINATOR_ID_CARRIER = 3; + int NOMINATOR_ID_SCORED = 4; + + @IntDef(prefix = { "NOMINATOR_ID_" }, value = { + NOMINATOR_ID_SAVED, + NOMINATOR_ID_SUGGESTION, + NOMINATOR_ID_PASSPOINT, + NOMINATOR_ID_CARRIER, + NOMINATOR_ID_SCORED}) @Retention(RetentionPolicy.SOURCE) - public @interface EvaluatorId {} + public @interface NominatorId {} /** - * Get the evaluator type. + * Get the nominator type. */ - @EvaluatorId int getId(); + @NominatorId int getId(); /** - * Get the evaluator name. + * Get the nominator name. */ String getName(); /** - * Update the evaluator. + * Update the nominator. * - * Certain evaluators have to be updated with the new scan results. For example - * the ScoredNetworkEvaluator needs to refresh its Score Cache. + * Certain nominators have to be updated with the new scan results. For example + * the ScoredNetworkNominator needs to refresh its Score Cache. * * @param scanDetails a list of scan details constructed from the scan results */ @@ -189,14 +186,11 @@ public class WifiNetworkSelector { * ephemeral networks are allowed * @param onConnectableListener callback to record all of the connectable networks * - * @return configuration of the chosen network; - * null if no network in this category is available. */ - @Nullable - WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, - WifiConfiguration currentNetwork, String currentBssid, - boolean connected, boolean untrustedNetworkAllowed, - OnConnectableListener onConnectableListener); + void nominateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, + boolean connected, boolean untrustedNetworkAllowed, + OnConnectableListener onConnectableListener); /** * Callback for recording connectable candidates @@ -207,13 +201,12 @@ public class WifiNetworkSelector { * * @param scanDetail describes the specific access point * @param config is the WifiConfiguration for the network - * @param score is the score assigned by the evaluator */ - void onConnectable(ScanDetail scanDetail, WifiConfiguration config, int score); + void onConnectable(ScanDetail scanDetail, WifiConfiguration config); } } - private final List<NetworkEvaluator> mEvaluators = new ArrayList<>(3); + private final List<NetworkNominator> mNominators = new ArrayList<>(3); // A helper to log debugging information in the local log buffer, which can // be retrieved in bugreport. @@ -380,8 +373,8 @@ public class WifiNetworkSelector { private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails, Set<String> bssidBlacklist, boolean isConnected, String currentBssid) { - ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>(); - List<ScanDetail> validScanDetails = new ArrayList<ScanDetail>(); + ArrayList<NetworkKey> unscoredNetworks = new ArrayList<>(); + List<ScanDetail> validScanDetails = new ArrayList<>(); StringBuffer noValidSsid = new StringBuffer(); StringBuffer blacklistedBssid = new StringBuffer(); StringBuffer lowRssi = new StringBuffer(); @@ -640,7 +633,7 @@ public class WifiNetworkSelector { } /** - * Overrides the {@code candidate} chosen by the {@link #mEvaluators} with the user chosen + * Overrides the {@code candidate} chosen by the {@link #mNominators} with the user chosen * {@link WifiConfiguration} if one exists. * * @return the user chosen {@link WifiConfiguration} if one exists, {@code candidate} otherwise @@ -715,9 +708,9 @@ public class WifiNetworkSelector { // Update all configured networks before initiating network selection. updateConfiguredNetworks(); - // Update the registered network evaluators. - for (NetworkEvaluator registeredEvaluator : mEvaluators) { - registeredEvaluator.update(scanDetails); + // Update the registered network nominators. + for (NetworkNominator registeredNominator : mNominators) { + registeredNominator.update(scanDetails); } // Filter out unwanted networks. @@ -730,43 +723,30 @@ public class WifiNetworkSelector { // Determine the weight for the last user selection final int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork(); final double lastSelectionWeight = calculateLastSelectionWeight(); - final ArraySet<Integer> mNetworkIds = new ArraySet<>(); - // Go through the registered network evaluators in order - WifiConfiguration selectedNetwork = null; WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard); if (currentNetwork != null) { wifiCandidates.setCurrent(currentNetwork.networkId, currentBssid); } - for (NetworkEvaluator registeredEvaluator : mEvaluators) { - localLog("About to run " + registeredEvaluator.getName() + " :"); - WifiConfiguration choice = registeredEvaluator.evaluateNetworks( + for (NetworkNominator registeredNominator : mNominators) { + localLog("About to run " + registeredNominator.getName() + " :"); + registeredNominator.nominateNetworks( new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected, untrustedNetworkAllowed, - (scanDetail, config, score) -> { + (scanDetail, config) -> { if (config != null) { mConnectableNetworks.add(Pair.create(scanDetail, config)); - mNetworkIds.add(config.networkId); wifiCandidates.add(scanDetail, config, - registeredEvaluator.getId(), - score, + registeredNominator.getId(), + 0, (config.networkId == lastUserSelectedNetworkId) ? lastSelectionWeight : 0.0, WifiConfiguration.isMetered(config, wifiInfo), predictThroughput(scanDetail)); mWifiMetrics.setNominatorForNetwork(config.networkId, - evaluatorIdToNominatorId(registeredEvaluator.getId())); + toProtoNominatorId(registeredNominator.getId())); } }); - if (choice != null && !mNetworkIds.contains(choice.networkId)) { - Log.wtf(TAG, registeredEvaluator.getName() - + " failed to report choice with noConnectibleListener"); - } - if (selectedNetwork == null && choice != null) { - selectedNetwork = choice; // First one wins - localLog(registeredEvaluator.getName() + " selects " - + WifiNetworkSelector.toNetworkString(selectedNetwork)); - } } if (mConnectableNetworks.size() != wifiCandidates.size()) { @@ -782,12 +762,12 @@ public class WifiNetworkSelector { WifiCandidates.Candidate best = null; for (WifiCandidates.Candidate candidate: group) { // Of all the candidates with the same networkId, choose the - // one with the smallest evaluatorId, and break ties by + // one with the smallest nominatorId, and break ties by // picking the one with the highest score. if (best == null - || candidate.getEvaluatorId() < best.getEvaluatorId() - || (candidate.getEvaluatorId() == best.getEvaluatorId() - && candidate.getEvaluatorScore() > best.getEvaluatorScore())) { + || candidate.getNominatorId() < best.getNominatorId() + || (candidate.getNominatorId() == best.getNominatorId() + && candidate.getNominatorScore() > best.getNominatorScore())) { best = candidate; } } @@ -795,18 +775,14 @@ public class WifiNetworkSelector { ScanDetail scanDetail = best.getScanDetail(); if (scanDetail != null) { mWifiConfigManager.setNetworkCandidateScanResult(best.getNetworkConfigId(), - scanDetail.getScanResult(), best.getEvaluatorScore()); + scanDetail.getScanResult(), best.getNominatorScore()); } } } ArrayMap<Integer, Integer> experimentNetworkSelections = new ArrayMap<>(); // for metrics - final int legacySelectedNetworkId = selectedNetwork == null - ? WifiConfiguration.INVALID_NETWORK_ID - : selectedNetwork.networkId; - - int selectedNetworkId = legacySelectedNetworkId; + int selectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; // Run all the CandidateScorers boolean legacyOverrideWanted = true; @@ -827,6 +803,7 @@ public class WifiNetworkSelector { chooses = " chooses "; legacyOverrideWanted = choice.userConnectChoiceOverride; selectedNetworkId = networkId; + updateChosenPasspointNetwork(choice); } String id = candidateScorer.getIdentifier(); int expid = experimentIdFromIdentifier(id); @@ -837,9 +814,7 @@ public class WifiNetworkSelector { } // Update metrics about differences in the selections made by various methods - final int activeExperimentId = activeScorer == null ? LEGACY_CANDIDATE_SCORER_EXP_ID - : experimentIdFromIdentifier(activeScorer.getIdentifier()); - experimentNetworkSelections.put(LEGACY_CANDIDATE_SCORER_EXP_ID, legacySelectedNetworkId); + final int activeExperimentId = experimentIdFromIdentifier(activeScorer.getIdentifier()); for (Map.Entry<Integer, Integer> entry : experimentNetworkSelections.entrySet()) { int experimentId = entry.getKey(); @@ -851,7 +826,8 @@ public class WifiNetworkSelector { } // Get a fresh copy of WifiConfiguration reflecting any scan result updates - selectedNetwork = mWifiConfigManager.getConfiguredNetwork(selectedNetworkId); + WifiConfiguration selectedNetwork = + mWifiConfigManager.getConfiguredNetwork(selectedNetworkId); if (selectedNetwork != null && legacyOverrideWanted) { selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork); mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); @@ -859,20 +835,32 @@ public class WifiNetworkSelector { return selectedNetwork; } - private static int evaluatorIdToNominatorId(@NetworkEvaluator.EvaluatorId int evaluatorId) { - switch (evaluatorId) { - case NetworkEvaluator.EVALUATOR_ID_SAVED: + private void updateChosenPasspointNetwork(WifiCandidates.ScoredCandidate choice) { + if (choice.candidateKey == null) { + return; + } + WifiConfiguration config = + mWifiConfigManager.getConfiguredNetwork(choice.candidateKey.networkId); + if (config.isPasspoint()) { + config.SSID = choice.candidateKey.matchInfo.networkSsid; + mWifiConfigManager.addOrUpdateNetwork(config, config.creatorUid, config.creatorName); + } + } + + private static int toProtoNominatorId(@NetworkNominator.NominatorId int nominatorId) { + switch (nominatorId) { + case NetworkNominator.NOMINATOR_ID_SAVED: return WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED; - case NetworkEvaluator.EVALUATOR_ID_SUGGESTION: + case NetworkNominator.NOMINATOR_ID_SUGGESTION: return WifiMetricsProto.ConnectionEvent.NOMINATOR_SUGGESTION; - case NetworkEvaluator.EVALUATOR_ID_PASSPOINT: + case NetworkNominator.NOMINATOR_ID_PASSPOINT: return WifiMetricsProto.ConnectionEvent.NOMINATOR_PASSPOINT; - case NetworkEvaluator.EVALUATOR_ID_CARRIER: + case NetworkNominator.NOMINATOR_ID_CARRIER: return WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER; - case NetworkEvaluator.EVALUATOR_ID_SCORED: + case NetworkNominator.NOMINATOR_ID_SCORED: return WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED; default: - Log.e(TAG, "UnrecognizedEvaluatorId" + evaluatorId); + Log.e(TAG, "UnrecognizedNominatorId" + nominatorId); return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN; } } @@ -932,13 +920,13 @@ public class WifiNetworkSelector { } /** - * Register a network evaluator + * Register a network nominator * - * @param evaluator the network evaluator to be registered + * @param nominator the network nominator to be registered * */ - public void registerNetworkEvaluator(@NonNull NetworkEvaluator evaluator) { - mEvaluators.add(Preconditions.checkNotNull(evaluator)); + public void registerNetworkNominator(@NonNull NetworkNominator nominator) { + mNominators.add(Preconditions.checkNotNull(nominator)); } /** diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index 42fac2912..26edf2e56 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -509,47 +509,52 @@ public class PasspointManager { } /** - * Find the best provider that can provide service through the given AP, which means the - * provider contained credential to authenticate with the given AP. + * Find all providers that can provide service through the given AP, which means the + * providers contained credential to authenticate with the given AP. * - * Here is the current precedence of the matching rule in descending order: - * 1. Home Provider - * 2. Roaming Provider + * If there is any home provider available, will return a list of matched home providers. + * Otherwise will return a list of matched roaming providers. * - * A {code null} will be returned if no matching is found. + * A empty list will be returned if no matching is found. * * @param scanResult The scan result associated with the AP - * @return A pair of {@link PasspointProvider} and match status. + * @return a list of pairs of {@link PasspointProvider} and match status. */ - public Pair<PasspointProvider, PasspointMatch> matchProvider(ScanResult scanResult) { + public @NonNull List<Pair<PasspointProvider, PasspointMatch>> matchProvider( + ScanResult scanResult) { List<Pair<PasspointProvider, PasspointMatch>> allMatches = getAllMatchedProviders( scanResult); - if (allMatches == null) { - return null; + if (allMatches.isEmpty()) { + return allMatches; } - Pair<PasspointProvider, PasspointMatch> bestMatch = null; + List<Pair<PasspointProvider, PasspointMatch>> homeProviders = new ArrayList<>(); + List<Pair<PasspointProvider, PasspointMatch>> roamingProviders = new ArrayList<>(); for (Pair<PasspointProvider, PasspointMatch> match : allMatches) { - if (!isExpired(match.first.getConfig())) { - if (match.second == PasspointMatch.HomeProvider) { - bestMatch = match; - break; - } - if (match.second == PasspointMatch.RoamingProvider && bestMatch == null) { - bestMatch = match; - } + if (isExpired(match.first.getConfig())) { + continue; } - } - if (bestMatch != null) { - Log.d(TAG, String.format("Matched %s to %s as %s", scanResult.SSID, - bestMatch.first.getConfig().getHomeSp().getFqdn(), - bestMatch.second == PasspointMatch.HomeProvider ? "Home Provider" - : "Roaming Provider")); - } else { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "No service provider found for " + scanResult.SSID); + if (match.second == PasspointMatch.HomeProvider) { + homeProviders.add(match); + } else { + roamingProviders.add(match); } } - return bestMatch; + + if (!homeProviders.isEmpty()) { + Log.d(TAG, String.format("Matched %s to %s providers as %s", scanResult.SSID, + homeProviders.size(), "Home Provider")); + return homeProviders; + } + if (!roamingProviders.isEmpty()) { + Log.d(TAG, String.format("Matched %s to %s providers as %s", scanResult.SSID, + allMatches.size(), "Roaming Provider")); + return roamingProviders; + } + + if (mVerboseLoggingEnabled) { + Log.d(TAG, "No service provider found for " + scanResult.SSID); + } + return new ArrayList<>(); } /** @@ -558,7 +563,7 @@ public class PasspointManager { * @param scanResult The scan result associated with the AP * @return a list of pairs of {@link PasspointProvider} and match status. */ - public List<Pair<PasspointProvider, PasspointMatch>> getAllMatchedProviders( + public @NonNull List<Pair<PasspointProvider, PasspointMatch>> getAllMatchedProviders( ScanResult scanResult) { List<Pair<PasspointProvider, PasspointMatch>> allMatches = new ArrayList<>(); @@ -1001,7 +1006,6 @@ public class PasspointManager { return true; } } - return false; } } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java deleted file mode 100644 index 6396f2c7c..000000000 --- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2016 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.server.wifi.hotspot2; - -import android.annotation.NonNull; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiConfiguration; -import android.os.Process; -import android.telephony.SubscriptionManager; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Pair; - -import com.android.server.wifi.NetworkUpdateResult; -import com.android.server.wifi.ScanDetail; -import com.android.server.wifi.WifiConfigManager; -import com.android.server.wifi.WifiInjector; -import com.android.server.wifi.WifiNetworkSelector; -import com.android.server.wifi.util.ScanResultUtil; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * This class is the WifiNetworkSelector.NetworkEvaluator implementation for - * Passpoint networks. - */ -public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { - private static final String NAME = "PasspointNetworkEvaluator"; - - private final PasspointManager mPasspointManager; - private final WifiConfigManager mWifiConfigManager; - private final LocalLog mLocalLog; - private final WifiInjector mWifiInjector; - private SubscriptionManager mSubscriptionManager; - /** - * Contained information for a Passpoint network candidate. - */ - private class PasspointNetworkCandidate { - PasspointNetworkCandidate(PasspointProvider provider, PasspointMatch matchStatus, - ScanDetail scanDetail) { - mProvider = provider; - mMatchStatus = matchStatus; - mScanDetail = scanDetail; - } - PasspointProvider mProvider; - PasspointMatch mMatchStatus; - ScanDetail mScanDetail; - } - - public PasspointNetworkEvaluator(PasspointManager passpointManager, - WifiConfigManager wifiConfigManager, LocalLog localLog, - WifiInjector wifiInjector, - SubscriptionManager subscriptionManager) { - mPasspointManager = passpointManager; - mWifiConfigManager = wifiConfigManager; - mLocalLog = localLog; - mWifiInjector = wifiInjector; - mSubscriptionManager = subscriptionManager; - } - - @Override - public @EvaluatorId int getId() { - return EVALUATOR_ID_PASSPOINT; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public void update(List<ScanDetail> scanDetails) {} - - @Override - public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, - WifiConfiguration currentNetwork, String currentBssid, - boolean connected, boolean untrustedNetworkAllowed, - @NonNull OnConnectableListener onConnectableListener) { - // Sweep the ANQP cache to remove any expired ANQP entries. - mPasspointManager.sweepCache(); - List<ScanDetail> filteredScanDetails = scanDetails.stream() - .filter(s -> s.getNetworkDetail().isInterworking()) - .filter(s -> { - if (!mWifiConfigManager.wasEphemeralNetworkDeleted( - ScanResultUtil.createQuotedSSID(s.getScanResult().SSID))) { - return true; - } else { - // If the user previously disconnects this network, don't select it. - mLocalLog.log("Ignoring disabled the SSID of Passpoint AP: " - + WifiNetworkSelector.toScanId(s.getScanResult())); - return false; - } - }).collect(Collectors.toList()); - - // Go through each ScanDetail and find the best provider for each ScanDetail. - List<PasspointNetworkCandidate> candidateList = new ArrayList<>(); - for (ScanDetail scanDetail : filteredScanDetails) { - ScanResult scanResult = scanDetail.getScanResult(); - - // Find the best provider for this ScanDetail. - Pair<PasspointProvider, PasspointMatch> bestProvider = - mPasspointManager.matchProvider(scanResult); - if (bestProvider != null) { - candidateList.add(new PasspointNetworkCandidate( - bestProvider.first, bestProvider.second, scanDetail)); - } - } - - // Done if no candidate is found. - if (candidateList.isEmpty()) { - localLog("No suitable Passpoint network found"); - return null; - } - - // Find the best Passpoint network among all candidates. - PasspointNetworkCandidate bestNetwork = - findBestNetwork(candidateList, currentNetwork == null ? null : currentNetwork.SSID); - - // Return the configuration for the current connected network if it is the best network. - if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID, - ScanResultUtil.createQuotedSSID(bestNetwork.mScanDetail.getSSID()))) { - localLog("Staying with current Passpoint network " + currentNetwork.SSID); - - // Update current network with the latest scan info. TODO - pull into common code - mWifiConfigManager.setNetworkCandidateScanResult(currentNetwork.networkId, - bestNetwork.mScanDetail.getScanResult(), 0); - mWifiConfigManager.updateScanDetailForNetwork(currentNetwork.networkId, - bestNetwork.mScanDetail); - onConnectableListener.onConnectable(bestNetwork.mScanDetail, currentNetwork, 0); - return currentNetwork; - } - - WifiConfiguration config = createWifiConfigForProvider(bestNetwork); - if (config != null) { - onConnectableListener.onConnectable(bestNetwork.mScanDetail, config, 0); - localLog("Passpoint network to connect to: " + config.SSID); - } - return config; - } - - /** - * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider. - * The newly created WifiConfiguration will also be added to WifiConfigManager. - * - * @param networkInfo Contained information for the Passpoint network to connect to - * @return {@link WifiConfiguration} - */ - private WifiConfiguration createWifiConfigForProvider(PasspointNetworkCandidate networkInfo) { - WifiConfiguration config = networkInfo.mProvider.getWifiConfig(); - config.SSID = ScanResultUtil.createQuotedSSID(networkInfo.mScanDetail.getSSID()); - if (networkInfo.mMatchStatus == PasspointMatch.HomeProvider) { - config.isHomeProviderNetwork = true; - } - - WifiConfiguration existingNetwork = mWifiConfigManager.getConfiguredNetwork( - config.getKey()); - if (existingNetwork != null) { - WifiConfiguration.NetworkSelectionStatus status = - existingNetwork.getNetworkSelectionStatus(); - if (!(status.isNetworkEnabled() - || mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId))) { - localLog("Current configuration for the Passpoint AP " + config.SSID - + " is disabled, skip this candidate"); - return null; - } - } - - // Add or update with the newly created WifiConfiguration to WifiConfigManager. - NetworkUpdateResult result; - if (config.fromWifiNetworkSuggestion) { - result = mWifiConfigManager.addOrUpdateNetwork( - config, config.creatorUid, config.creatorName); - } else { - result = mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); - } - if (!result.isSuccess()) { - localLog("Failed to add passpoint network"); - return existingNetwork; - } - - mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID, null); - mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(), - networkInfo.mScanDetail.getScanResult(), 0); - mWifiConfigManager.updateScanDetailForNetwork( - result.getNetworkId(), networkInfo.mScanDetail); - return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); - } - - /** - * Given a list of Passpoint networks (with both provider and scan info), find and return - * the one with highest score. The score is calculated using - * {@link PasspointNetworkScore#calculateScore}. - * - * @param networkList List of Passpoint networks - * @param currentNetworkSsid The SSID of the currently connected network, null if not connected - * @return {@link PasspointNetworkCandidate} - */ - private PasspointNetworkCandidate findBestNetwork( - List<PasspointNetworkCandidate> networkList, String currentNetworkSsid) { - PasspointNetworkCandidate bestCandidate = null; - int bestScore = Integer.MIN_VALUE; - for (PasspointNetworkCandidate candidate : networkList) { - ScanDetail scanDetail = candidate.mScanDetail; - PasspointMatch match = candidate.mMatchStatus; - - boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid, - ScanResultUtil.createQuotedSSID(scanDetail.getSSID())); - int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider, - scanDetail, mPasspointManager.getANQPElements(scanDetail.getScanResult()), - isActiveNetwork); - - if (score > bestScore) { - bestCandidate = candidate; - bestScore = score; - } - } - localLog("Best Passpoint network " + bestCandidate.mScanDetail.getSSID() + " provided by " - + bestCandidate.mProvider.getConfig().getHomeSp().getFqdn()); - return bestCandidate; - } - - private void localLog(String log) { - mLocalLog.log(log); - } -} diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominator.java new file mode 100644 index 000000000..7dbce2290 --- /dev/null +++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominator.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2016 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.server.wifi.hotspot2; + +import android.annotation.NonNull; +import android.net.wifi.WifiConfiguration; +import android.os.Process; +import android.telephony.SubscriptionManager; +import android.util.LocalLog; +import android.util.Pair; + +import com.android.server.wifi.NetworkUpdateResult; +import com.android.server.wifi.ScanDetail; +import com.android.server.wifi.WifiConfigManager; +import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiNetworkSelector; +import com.android.server.wifi.hotspot2.anqp.ANQPElement; +import com.android.server.wifi.hotspot2.anqp.Constants; +import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement; +import com.android.server.wifi.util.ScanResultUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * This class is the WifiNetworkSelector.NetworkNominator implementation for + * Passpoint networks. + */ +public class PasspointNetworkNominator implements WifiNetworkSelector.NetworkNominator { + private static final String NAME = "PasspointNetworkNominator"; + + private final PasspointManager mPasspointManager; + private final WifiConfigManager mWifiConfigManager; + private final LocalLog mLocalLog; + private final WifiInjector mWifiInjector; + private SubscriptionManager mSubscriptionManager; + /** + * Contained information for a Passpoint network candidate. + */ + private class PasspointNetworkCandidate { + PasspointNetworkCandidate(PasspointProvider provider, PasspointMatch matchStatus, + ScanDetail scanDetail) { + mProvider = provider; + mMatchStatus = matchStatus; + mScanDetail = scanDetail; + } + PasspointProvider mProvider; + PasspointMatch mMatchStatus; + ScanDetail mScanDetail; + } + + public PasspointNetworkNominator(PasspointManager passpointManager, + WifiConfigManager wifiConfigManager, LocalLog localLog, + WifiInjector wifiInjector, + SubscriptionManager subscriptionManager) { + mPasspointManager = passpointManager; + mWifiConfigManager = wifiConfigManager; + mLocalLog = localLog; + mWifiInjector = wifiInjector; + mSubscriptionManager = subscriptionManager; + } + + @Override + public @NominatorId int getId() { + return NOMINATOR_ID_PASSPOINT; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void update(List<ScanDetail> scanDetails) {} + + @Override + public void nominateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, + boolean connected, boolean untrustedNetworkAllowed, + @NonNull OnConnectableListener onConnectableListener) { + // Sweep the ANQP cache to remove any expired ANQP entries. + mPasspointManager.sweepCache(); + List<ScanDetail> filteredScanDetails = new ArrayList<>(); + // Filter out all invalid scanDetail + for (ScanDetail scanDetail : scanDetails) { + if (scanDetail.getNetworkDetail() == null + || !scanDetail.getNetworkDetail().isInterworking()) { + // If scanDetail is not passpoint network, ignore. + continue; + } + + if (!scanDetail.getNetworkDetail().isInternet() + || isApWanLinkStatusDown(scanDetail)) { + // If scanDetail has no internet connection, ignore. + mLocalLog.log("Ignoring no internet connection Passpoint AP: " + + WifiNetworkSelector.toScanId(scanDetail.getScanResult())); + continue; + } + + if (mWifiConfigManager.wasEphemeralNetworkDeleted( + ScanResultUtil.createQuotedSSID(scanDetail.getScanResult().SSID))) { + // If the user previously disconnects this network, don't select it. + mLocalLog.log("Ignoring disabled the SSID of Passpoint AP: " + + WifiNetworkSelector.toScanId(scanDetail.getScanResult())); + continue; + } + filteredScanDetails.add(scanDetail); + } + + List<Pair<ScanDetail, WifiConfiguration>> matchedCandidates = + findBestMatchScanDetailForProviders(filteredScanDetails); + for (Pair<ScanDetail, WifiConfiguration> candidate : matchedCandidates) { + onConnectableListener.onConnectable(candidate.first, candidate.second); + } + } + + /** + * Check if ANQP element inside that scanDetail indicate AP WAN port link status is down. + * + * @param scanDetail contains ANQP element to check. + * @return return true is link status is down, otherwise return false. + */ + private boolean isApWanLinkStatusDown(ScanDetail scanDetail) { + Map<Constants.ANQPElementType, ANQPElement> anqpElements = + mPasspointManager.getANQPElements(scanDetail.getScanResult()); + if (anqpElements == null) { + return false; + } + HSWanMetricsElement wm = (HSWanMetricsElement) anqpElements.get( + Constants.ANQPElementType.HSWANMetrics); + if (wm == null) { + return false; + } + return wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped(); + } + + /** + * Match available providers for each scan detail. Then for each available provider, find the + * best scan detail for it. + * @param scanDetails all details for this scan. + * @return List of pair of WifiConfig from available provider and matched scanDetail. + */ + private @NonNull List<Pair<ScanDetail, WifiConfiguration>> findBestMatchScanDetailForProviders( + List<ScanDetail> scanDetails) { + List<Pair<ScanDetail, WifiConfiguration>> results = new ArrayList<>(); + Map<PasspointProvider, List<PasspointNetworkCandidate>> candidatesPerProvider = + new HashMap<>(); + // Match each scanDetail with best provider (home > roaming), and grouped by FQDN. + for (ScanDetail scanDetail : scanDetails) { + List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = + mPasspointManager.matchProvider(scanDetail.getScanResult()); + if (matchedProviders == null) { + continue; + } + for (Pair<PasspointProvider, PasspointMatch> matchedProvider : matchedProviders) { + List<PasspointNetworkCandidate> candidates = candidatesPerProvider + .computeIfAbsent(matchedProvider.first, k -> new ArrayList<>()); + candidates.add(new PasspointNetworkCandidate(matchedProvider.first, + matchedProvider.second, scanDetail)); + } + } + // For each provider find the best scanDetail for it and create selection candidate pair. + for (List<PasspointNetworkCandidate> candidates : candidatesPerProvider.values()) { + List<PasspointNetworkCandidate> bestCandidates = findHomeNetworksIfPossible(candidates); + for (PasspointNetworkCandidate candidate : bestCandidates) { + WifiConfiguration config = createWifiConfigForProvider(candidate); + if (config == null) { + continue; + } + results.add(Pair.create(candidate.mScanDetail, config)); + } + } + return results; + } + + /** + * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider. + * The newly created WifiConfiguration will also be added to WifiConfigManager. + * + * @return {@link WifiConfiguration} + */ + private WifiConfiguration createWifiConfigForProvider( + PasspointNetworkCandidate candidate) { + WifiConfiguration config = candidate.mProvider.getWifiConfig(); + config.SSID = ScanResultUtil.createQuotedSSID(candidate.mScanDetail.getSSID()); + if (candidate.mMatchStatus == PasspointMatch.HomeProvider) { + config.isHomeProviderNetwork = true; + } + + WifiConfiguration existingNetwork = mWifiConfigManager.getConfiguredNetwork( + config.getKey()); + if (existingNetwork != null) { + WifiConfiguration.NetworkSelectionStatus status = + existingNetwork.getNetworkSelectionStatus(); + if (!(status.isNetworkEnabled() + || mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId))) { + mLocalLog.log("Current configuration for the Passpoint AP " + config.SSID + + " is disabled, skip this candidate"); + return null; + } + } + + // Add or update with the newly created WifiConfiguration to WifiConfigManager. + NetworkUpdateResult result; + if (config.fromWifiNetworkSuggestion) { + result = mWifiConfigManager.addOrUpdateNetwork( + config, config.creatorUid, config.creatorName); + } else { + result = mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); + } + if (!result.isSuccess()) { + mLocalLog.log("Failed to add passpoint network"); + return existingNetwork; + } + + mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID, null); + mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(), + candidate.mScanDetail.getScanResult(), 0); + mWifiConfigManager.updateScanDetailForNetwork( + result.getNetworkId(), candidate.mScanDetail); + return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); + } + + /** + * Given a list of Passpoint networks (with both provider and scan info), return all + * homeProvider matching networks if there is any, otherwise return all roamingProvider matching + * networks. + * + * @param networkList List of Passpoint networks + * @return List of {@link PasspointNetworkCandidate} + */ + private @NonNull List<PasspointNetworkCandidate> findHomeNetworksIfPossible( + @NonNull List<PasspointNetworkCandidate> networkList) { + List<PasspointNetworkCandidate> homeProviderCandidates = networkList.stream() + .filter(candidate -> candidate.mMatchStatus == PasspointMatch.HomeProvider) + .collect(Collectors.toList()); + if (homeProviderCandidates.isEmpty()) { + return networkList; + } + return homeProviderCandidates; + } +} |