diff options
author | Randy Pan <zpan@google.com> | 2016-09-27 21:49:38 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-09-27 21:49:38 +0000 |
commit | 8ef24fce3bac0c107324d6f8592a1215772ab397 (patch) | |
tree | 038c66834e693f2782527042b84f3992c2886e3e | |
parent | e1103ae70435f0aac61d167aeada6f1862d5b907 (diff) | |
parent | 57c8c1165d5ea5c10cd96ea51652c11db7635302 (diff) |
Merge changes from topic 'WifiNetworkSelector'
* changes:
Unit tests for ExternalScoreEvaluator
Unit tests for SavedNetworkEvaluator
WifiNetworkSelector unit tests.
WCM: unit tests
Wifi Network Selector
12 files changed, 3008 insertions, 3601 deletions
diff --git a/service/java/com/android/server/wifi/ExternalScoreEvaluator.java b/service/java/com/android/server/wifi/ExternalScoreEvaluator.java new file mode 100644 index 000000000..03d89b040 --- /dev/null +++ b/service/java/com/android/server/wifi/ExternalScoreEvaluator.java @@ -0,0 +1,369 @@ +/* + * 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.Nullable; +import android.content.Context; +import android.net.NetworkKey; +import android.net.NetworkScoreManager; +import android.net.WifiKey; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.os.Process; +import android.util.LocalLog; +import android.util.Log; +import android.util.Pair; + +import com.android.server.wifi.util.ScanResultUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * This class is the WifiNetworkSelector.NetworkEvaluator implementation for + * externally scored networks. + */ +public class ExternalScoreEvaluator implements WifiNetworkSelector.NetworkEvaluator { + private static final String NAME = "WifiExternalScoreEvaluator"; + private static final String TAG = NAME; + private final WifiConfigManager mWifiConfigManager; + private final Clock mClock; + private final LocalLog mLocalLog; + private final NetworkScoreManager mScoreManager; + private final WifiNetworkScoreCache mScoreCache; + + ExternalScoreEvaluator(Context context, WifiConfigManager configManager, Clock clock, + LocalLog localLog) { + mWifiConfigManager = configManager; + mClock = clock; + mLocalLog = localLog; + mScoreManager = + (NetworkScoreManager) context.getSystemService(Context.NETWORK_SCORE_SERVICE); + if (mScoreManager != null) { + mScoreCache = new WifiNetworkScoreCache(context); + mScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); + } else { + Log.e(TAG, "Couldn't get NETWORK_SCORE_SERVICE."); + mScoreCache = null; + } + } + + private void localLog(String log) { + if (mLocalLog != null) { + mLocalLog.log(log); + } + } + + /** + * Get the evaluator name. + */ + public String getName() { + return NAME; + } + + private void updateNetworkScoreCache(List<ScanDetail> scanDetails) { + ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>(); + + for (ScanDetail scanDetail : scanDetails) { + ScanResult scanResult = scanDetail.getScanResult(); + + // Is there a score for this network? If not, request a score. + if (mScoreCache != null && !mScoreCache.isScoredNetwork(scanResult)) { + WifiKey wifiKey; + + try { + wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID); + NetworkKey ntwkKey = new NetworkKey(wifiKey); + unscoredNetworks.add(ntwkKey); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Invalid SSID=" + scanResult.SSID + " BSSID=" + scanResult.BSSID + + " for network score. Skip."); + } + } + } + + // Kick the score manager if there is any unscored network. + if (mScoreManager != null && unscoredNetworks.size() != 0) { + NetworkKey[] unscoredNetworkKeys = + unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]); + mScoreManager.requestScores(unscoredNetworkKeys); + } + } + + /** + * Update the evaluator. + */ + public void update(List<ScanDetail> scanDetails) { + updateNetworkScoreCache(scanDetails); + } + + private boolean isPotentialEphemeralNetwork(List<WifiConfiguration> associatedConfigurations) { + if (associatedConfigurations == null) { + return true; + } else if (associatedConfigurations.size() == 1) { + // If there is more than one associated networks, it must be a passpoint network. + // Hence it is not a ephemeral network. + WifiConfiguration network = associatedConfigurations.get(0); + if (network.ephemeral) { + return true; + } + } + return false; + } + + private WifiConfiguration getPotentialEphemeralNetworkConfiguration( + List<WifiConfiguration> associatedConfigurations) { + if (associatedConfigurations == null) { + return null; + } else { + WifiConfiguration network = associatedConfigurations.get(0); + return network; + } + } + + /** + * Returns the available external network score or null if no score is available. + * + * @param scanResult The scan result of the network to score. + * @return A valid external score if one is available or NULL. + */ + @Nullable + Integer getNetworkScore(ScanResult scanResult, WifiNetworkScoreCache scoreCache) { + if (scoreCache != null && scoreCache.isScoredNetwork(scanResult)) { + int score = scoreCache.getNetworkScore(scanResult, false); + localLog(WifiNetworkSelector.toScanId(scanResult) + " has score: " + score); + return score; + } + return null; + } + + /** + * Returns the best candidate network according to the given ExternalScoreEvaluator. + */ + @Nullable + WifiConfiguration getExternalScoreCandidate(ExternalScoreTracker scoreTracker, + WifiNetworkScoreCache scoreCache) { + int candidateNetworkId = WifiConfiguration.INVALID_NETWORK_ID; + switch (scoreTracker.getBestCandidateType()) { + case ExternalScoreTracker.EXTERNAL_SCORED_UNTRUSTED_NETWORK: + ScanResult untrustedScanResultCandidate = + scoreTracker.getScanResultCandidate(); + WifiConfiguration unTrustedNetworkCandidate = + ScanResultUtil.createNetworkFromScanResult(untrustedScanResultCandidate); + + // Mark this config as ephemeral so it isn't persisted. + unTrustedNetworkCandidate.ephemeral = true; + if (scoreCache != null) { + unTrustedNetworkCandidate.meteredHint = + scoreCache.getMeteredHint(untrustedScanResultCandidate); + } + NetworkUpdateResult result = + mWifiConfigManager.addOrUpdateNetwork(unTrustedNetworkCandidate, + Process.WIFI_UID); + if (!result.isSuccess()) { + Log.e(TAG, "Failed to add ephemeral network"); + break; + } + candidateNetworkId = result.getNetworkId(); + mWifiConfigManager.setNetworkCandidateScanResult(candidateNetworkId, + untrustedScanResultCandidate, 0); + localLog(String.format("new ephemeral candidate %s network ID:%d, " + + "meteredHint=%b", + WifiNetworkSelector.toScanId(untrustedScanResultCandidate), + candidateNetworkId, + unTrustedNetworkCandidate.meteredHint)); + break; + + case ExternalScoreTracker.EXTERNAL_SCORED_SAVED_NETWORK: + ScanResult scanResultCandidate = scoreTracker.getScanResultCandidate(); + candidateNetworkId = scoreTracker.getSavedConfig().networkId; + mWifiConfigManager.setNetworkCandidateScanResult(candidateNetworkId, + scanResultCandidate, 0); + localLog(String.format("new saved network candidate %s network ID:%d", + WifiNetworkSelector.toScanId(scanResultCandidate), + candidateNetworkId)); + break; + + case ExternalScoreTracker.EXTERNAL_SCORED_NONE: + localLog("did not see any good candidates."); + break; + + default: + localLog("Unhandled case. No candidate selected."); + break; + } + return mWifiConfigManager.getConfiguredNetwork(candidateNetworkId); + } + + /** + * 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. + */ + public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, boolean connected, + boolean untrustedNetworkAllowed, + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) { + if (mScoreCache == null) { + localLog("has no network score cache."); + return null; + } + + final ExternalScoreTracker externalScoreTracker = new ExternalScoreTracker(mLocalLog); + ArrayList<NetworkKey> unscoredNetworks = new ArrayList<>(); + + for (ScanDetail scanDetail : scanDetails) { + ScanResult scanResult = scanDetail.getScanResult(); + + // One ScanResult can be associated with more than one networks, hence we calculate all + // the scores and use the highest one as the ScanResult's score. + // TODO(b/31065385): WifiConfigManager does not support passpoint networks currently. + // So this list has just one entry always. + List<WifiConfiguration> associatedConfigs = null; + WifiConfiguration associatedConfig = + mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail); + if (associatedConfig != null) { + associatedConfigs = + new ArrayList<>(Arrays.asList(associatedConfig)); + } + + if (isPotentialEphemeralNetwork(associatedConfigs)) { + if (untrustedNetworkAllowed) { + if (!mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) { + Integer score = getNetworkScore(scanResult, mScoreCache); + externalScoreTracker.trackUntrustedCandidate(score, scanResult); + if (connectableNetworks != null) { + connectableNetworks.add(Pair.create(scanDetail, + getPotentialEphemeralNetworkConfiguration(associatedConfigs))); + } + } + } + continue; + } + + for (WifiConfiguration network : associatedConfigs) { + WifiConfiguration.NetworkSelectionStatus status = + network.getNetworkSelectionStatus(); + 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; + } + + // Saved network wth an external score. + if (network.useExternalScores) { + localLog("Network " + WifiNetworkSelector.toNetworkString(network) + + " uses external score"); + Integer score = getNetworkScore(scanResult, mScoreCache); + externalScoreTracker.trackSavedCandidate(score, network, scanResult); + if (connectableNetworks != null) { + connectableNetworks.add(Pair.create(scanDetail, network)); + } + } + } + } + + WifiConfiguration candidate = getExternalScoreCandidate(externalScoreTracker, mScoreCache); + + if (candidate != null + && candidate.getNetworkSelectionStatus().getCandidate() != null) { + return candidate; + } else { + return null; + } + } + + /** + * Used to track the network with the highest score. + */ + static class ExternalScoreTracker { + public static final int EXTERNAL_SCORED_NONE = 0; + public static final int EXTERNAL_SCORED_SAVED_NETWORK = 1; + public static final int EXTERNAL_SCORED_UNTRUSTED_NETWORK = 2; + + private int mBestCandidateType = EXTERNAL_SCORED_NONE; + private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE; + private WifiConfiguration mSavedConfig; + private ScanResult mScanResultCandidate; + private final LocalLog mLocalLog; + + ExternalScoreTracker(LocalLog localLog) { + mLocalLog = localLog; + } + + // Determines whether or not the given scan result is the best one its seen so far. + void trackUntrustedCandidate(@Nullable Integer score, ScanResult scanResult) { + if (score != null && score > mHighScore) { + mHighScore = score; + mScanResultCandidate = scanResult; + mBestCandidateType = EXTERNAL_SCORED_UNTRUSTED_NETWORK; + localLog(WifiNetworkSelector.toScanId(scanResult) + + " becomes the new untrusted candidate."); + } + } + + // Determines whether or not the given saved network is the best one its seen so far. + void trackSavedCandidate(@Nullable Integer score, WifiConfiguration config, + ScanResult scanResult) { + // Always take the highest score. If there's a tie and an untrusted network is currently + // the best then pick the saved network. + if (score != null + && (score > mHighScore + || (mBestCandidateType == EXTERNAL_SCORED_UNTRUSTED_NETWORK + && score == mHighScore))) { + mHighScore = score; + mSavedConfig = config; + mScanResultCandidate = scanResult; + mBestCandidateType = EXTERNAL_SCORED_SAVED_NETWORK; + localLog(WifiNetworkSelector.toScanId(scanResult) + + " becomes the new externally scored saved network candidate."); + } + } + + int getBestCandidateType() { + return mBestCandidateType; + } + + int getHighScore() { + return mHighScore; + } + + public ScanResult getScanResultCandidate() { + return mScanResultCandidate; + } + + WifiConfiguration getSavedConfig() { + return mSavedConfig; + } + + private void localLog(String log) { + if (mLocalLog != null) { + mLocalLog.log(log); + } + } + } +} diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java new file mode 100644 index 000000000..afd9b583c --- /dev/null +++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java @@ -0,0 +1,343 @@ +/* + * 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.content.Context; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.util.LocalLog; +import android.util.Pair; + +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.Arrays; +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 = "WifiSavedNetworkEvaluator"; + private final WifiConfigManager mWifiConfigManager; + private final Clock mClock; + private final LocalLog mLocalLog; + private final int mRssiScoreSlope; + private final int mRssiScoreOffset; + private final int mSameBssidAward; + private final int mSameNetworkAward; + private final int mBand5GHzAward; + private final int mLastSelectionAward; + private final int mPasspointSecurityAward; + private final int mSecurityAward; + private final int mNoInternetPenalty; + private final int mThresholdSaturatedRssi24; + + SavedNetworkEvaluator(Context context, WifiConfigManager configManager, + Clock clock, LocalLog localLog) { + mWifiConfigManager = configManager; + mClock = clock; + mLocalLog = localLog; + + mRssiScoreSlope = context.getResources().getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); + mRssiScoreOffset = context.getResources().getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_OFFSET); + mSameBssidAward = context.getResources().getInteger( + R.integer.config_wifi_framework_SAME_BSSID_AWARD); + mSameNetworkAward = context.getResources().getInteger( + R.integer.config_wifi_framework_current_network_boost); + mLastSelectionAward = context.getResources().getInteger( + R.integer.config_wifi_framework_LAST_SELECTION_AWARD); + mPasspointSecurityAward = context.getResources().getInteger( + R.integer.config_wifi_framework_PASSPOINT_SECURITY_AWARD); + mSecurityAward = context.getResources().getInteger( + R.integer.config_wifi_framework_SECURITY_AWARD); + mBand5GHzAward = context.getResources().getInteger( + R.integer.config_wifi_framework_5GHz_preference_boost_factor); + mThresholdSaturatedRssi24 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); + mNoInternetPenalty = (mThresholdSaturatedRssi24 + mRssiScoreOffset) + * mRssiScoreSlope + mBand5GHzAward + mSameNetworkAward + + mSameBssidAward + mSecurityAward; + } + + private void localLog(String log) { + if (mLocalLog != null) { + mLocalLog.log(log); + } + } + + /** + * Get the evaluator name. + */ + public String getName() { + return NAME; + } + + /** + * Update all the saved networks' selection status + */ + private void updateSavedNetworkSelectionStatus() { + List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks(); + if (savedNetworks.size() == 0) { + localLog("No saved networks."); + return; + } + + StringBuffer sbuf = new StringBuffer("Saved Networks List: \n"); + for (WifiConfiguration network : savedNetworks) { + WifiConfiguration.NetworkSelectionStatus status = + network.getNetworkSelectionStatus(); + + // If a configuration is temporarily disabled, re-enable it before trying + // to connect to it. + mWifiConfigManager.tryEnableNetwork(network.networkId); + + //TODO(b/30928589): Enable "permanently" disabled networks if we are in DISCONNECTED + // state. + + // Clear the cached candidate, score and seen. + mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); + + sbuf.append(" ").append(WifiNetworkSelector.toNetworkString(network)).append(" ") + .append(" User Preferred BSSID: ").append(network.BSSID) + .append(" FQDN: ").append(network.FQDN).append(" ") + .append(status.getNetworkStatusString()).append(" Disable account: "); + for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE; + index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; + index++) { + sbuf.append(status.getDisableReasonCounter(index)).append(" "); + } + sbuf.append("Connect Choice: ").append(status.getConnectChoice()) + .append(" set time: ").append(status.getConnectChoiceTimestamp()) + .append("\n"); + } + localLog(sbuf.toString()); + } + + /** + * Update the evaluator. + */ + public void update(List<ScanDetail> scanDetails) { + updateSavedNetworkSelectionStatus(); + } + + private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network, + WifiConfiguration currentNetwork, String currentBssid, + StringBuffer sbuf) { + int score = 0; + + sbuf.append("[ ").append(scanResult).append("] "); + // Calculate the RSSI score. + int rssi = scanResult.level <= mThresholdSaturatedRssi24 + ? scanResult.level : mThresholdSaturatedRssi24; + score += (rssi + mRssiScoreOffset) * mRssiScoreSlope; + sbuf.append(" RSSI score: ").append(score).append(","); + + // 5GHz band bonus. + if (scanResult.is5GHz()) { + score += mBand5GHzAward; + sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward) + .append(","); + } + + // Last user selection award. + int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork(); + WifiConfiguration lastUserSelectedNetwork = + mWifiConfigManager.getConfiguredNetwork(lastUserSelectedNetworkId); + if (lastUserSelectedNetwork != null && lastUserSelectedNetworkId == network.networkId) { + long timeDifference = mClock.getElapsedSinceBootMillis() + - mWifiConfigManager.getLastSelectedTimeStamp(); + if (timeDifference > 0) { + int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60); + score += bonus > 0 ? bonus : 0; + sbuf.append(" User selected it last time ").append(timeDifference / 1000 / 60) + .append(" minutes ago, bonus: ").append(bonus).append(","); + } + } + + // Same network award. + if (currentNetwork != null + && (network == currentNetwork || network.isLinked(currentNetwork))) { + score += mSameNetworkAward; + sbuf.append(" Same network bonus as the current one bonus: ") + .append(mSameNetworkAward).append(","); + } + + // Same BSSID award. + if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) { + score += mSameBssidAward; + sbuf.append(" Same BSSID as the current one bonus: ").append(mSameBssidAward) + .append(","); + } + + // Security award. + if (network.isPasspoint()) { + score += mPasspointSecurityAward; + sbuf.append(" Passpoint bonus: ").append(mPasspointSecurityAward).append(","); + } else if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) { + score += mSecurityAward; + sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(","); + } + + // No internet penalty. + if (network.numNoInternetAccessReports > 0 && !network.validatedInternetAccess) { + score -= mNoInternetPenalty; + sbuf.append(" No internet penalty: -").append(mNoInternetPenalty).append(","); + } + + sbuf.append(" ## Total score: ").append(score).append("\n"); + + return score; + } + + private WifiConfiguration adjustCandidateWithUserSelection(WifiConfiguration candidate, + ScanResult scanResultCandidate) { + WifiConfiguration tempConfig = candidate; + + while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) { + String key = tempConfig.getNetworkSelectionStatus().getConnectChoice(); + tempConfig = mWifiConfigManager.getConfiguredNetwork(key); + + if (tempConfig != null) { + WifiConfiguration.NetworkSelectionStatus tempStatus = + tempConfig.getNetworkSelectionStatus(); + if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) { + scanResultCandidate = tempStatus.getCandidate(); + candidate = tempConfig; + } + } else { + localLog("Connect choice: " + key + " has no corresponding saved config."); + break; + } + } + localLog("After user selection adjustment, the final candidate is:" + + WifiNetworkSelector.toNetworkString(candidate) + " : " + + scanResultCandidate.BSSID); + return candidate; + } + + /** + * 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. + */ + public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, boolean connected, + boolean untrustedNetworkAllowed, + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) { + int highestScore = Integer.MIN_VALUE; + ScanResult scanResultCandidate = null; + WifiConfiguration candidate = null; + StringBuffer scoreHistory = new StringBuffer(); + + for (ScanDetail scanDetail : scanDetails) { + ScanResult scanResult = scanDetail.getScanResult(); + int highestScoreOfScanResult = Integer.MIN_VALUE; + int score; + int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID; + + // One ScanResult can be associated with more than one networks, hence we calculate all + // the scores and use the highest one as the ScanResult's score. + // TODO(b/31065385): WifiConfigManager does not support passpoint networks currently. + // So this list has just one entry always. + List<WifiConfiguration> associatedConfigurations = null; + WifiConfiguration associatedConfiguration = + mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail); + + if (associatedConfiguration == null) { + continue; + } else { + associatedConfigurations = + new ArrayList<>(Arrays.asList(associatedConfiguration)); + } + + for (WifiConfiguration network : associatedConfigurations) { + WifiConfiguration.NetworkSelectionStatus status = + network.getNetworkSelectionStatus(); + 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; + } + + // If the network is marked to use external scores, leave it to the + // external score evaluator to handle it. + if (network.useExternalScores) { + localLog("Network " + WifiNetworkSelector.toNetworkString(network) + + " has external score."); + continue; + } + + score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid, + scoreHistory); + + if (score > highestScoreOfScanResult) { + highestScoreOfScanResult = score; + candidateIdOfScanResult = network.networkId; + } + + if (score > status.getCandidateScore() || (score == status.getCandidateScore() + && status.getCandidate() != null + && scanResult.level > status.getCandidate().level)) { + mWifiConfigManager.setNetworkCandidateScanResult( + candidateIdOfScanResult, scanResult, score); + } + } + + if (connectableNetworks != null) { + connectableNetworks.add(Pair.create(scanDetail, + mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult))); + } + + if (highestScoreOfScanResult > highestScore + || (highestScoreOfScanResult == highestScore + && scanResultCandidate != null + && scanResult.level > scanResultCandidate.level)) { + highestScore = highestScoreOfScanResult; + scanResultCandidate = scanResult; + mWifiConfigManager.setNetworkCandidateScanResult( + candidateIdOfScanResult, scanResultCandidate, highestScore); + // Reload the network config with the updated info. + candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult); + } + } + + if (scoreHistory.length() > 0) { + localLog("\n" + scoreHistory.toString()); + } + + if (scanResultCandidate != null) { + return adjustCandidateWithUserSelection(candidate, scanResultCandidate); + } else { + localLog("did not see any good candidates."); + return null; + } + } +} diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index d570f81ec..3c2971210 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -18,7 +18,6 @@ package com.android.server.wifi; import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; -import android.app.ActivityManager; import android.app.AlarmManager; import android.content.Context; import android.net.wifi.ScanResult; @@ -37,8 +36,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.util.ScanResultUtil; -import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; @@ -50,7 +47,8 @@ import java.util.Set; * * When the screen is turned on or off, WiFi is connected or disconnected, * or on-demand, a scan is initiatiated and the scan results are passed - * to QNS for it to make a recommendation on which network to connect to. + * to WifiNetworkSelector for it to make a recommendation on which network + * to connect to. */ public class WifiConnectivityManager { public static final String WATCHDOG_TIMER_TAG = @@ -83,8 +81,8 @@ public class WifiConnectivityManager { // PNO scan interval in milli-seconds. This is the scan // performed when screen is off and connected. private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds - // When a network is found by PNO scan but gets rejected by QNS due to its - // low RSSI value, scan will be reschduled in an exponential back off manner. + // When a network is found by PNO scan but gets rejected by Wifi Network Selector due + // to its low RSSI value, scan will be reschduled in an exponential back off manner. private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds // Maximum number of retries when starting a scan failed @@ -118,24 +116,21 @@ public class WifiConnectivityManager { public static final int WIFI_STATE_DISCONNECTED = 2; public static final int WIFI_STATE_TRANSITIONING = 3; - // Due to b/28020168, timer based single scan will be scheduled - // to provide periodic scan in an exponential backoff fashion. - private static final boolean ENABLE_BACKGROUND_SCAN = false; - // Flag to turn on connected PNO, when needed - private static final boolean ENABLE_CONNECTED_PNO_SCAN = false; + // Saved network evaluator priority + private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1; + private static final int EXTERNAL_SCORE_EVALUATOR_PRIORITY = 2; private final WifiStateMachine mStateMachine; private final WifiScanner mScanner; private final WifiConfigManager mConfigManager; private final WifiInfo mWifiInfo; - private final WifiQualifiedNetworkSelector mQualifiedNetworkSelector; + private final WifiNetworkSelector mNetworkSelector; private final WifiLastResortWatchdog mWifiLastResortWatchdog; private final WifiMetrics mWifiMetrics; private final AlarmManager mAlarmManager; private final Handler mEventHandler; private final Clock mClock; - private final LocalLog mLocalLog = - new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256); + private final LocalLog mLocalLog; private final LinkedList<Long> mConnectionAttemptTimeStamps; private boolean mDbg = false; @@ -166,7 +161,9 @@ public class WifiConnectivityManager { // A helper to log debugging information in the local log buffer, which can // be retrieved in bugreport. private void localLog(String log) { - mLocalLog.log(log); + if (mLocalLog != null) { + mLocalLog.log(log); + } } // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times @@ -216,21 +213,28 @@ public class WifiConnectivityManager { * Executes selection of potential network candidates, initiation of connection attempt to that * network. * - * @return true - if a candidate is selected by QNS - * false - if no candidate is selected by QNS + * @return true - if a candidate is selected by WifiNetworkSelector + * false - if no candidate is selected by WifiNetworkSelector */ private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) { - localLog(listenerName + " onResults: start QNS"); + if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) { + localLog(listenerName + " onResults: No network selection because linkDebouncing is " + + mStateMachine.isLinkDebouncing() + " and supplicantTransient is " + + mStateMachine.isSupplicantTransientState()); + return false; + } + + localLog(listenerName + " onResults: start network selection"); + WifiConfiguration candidate = - mQualifiedNetworkSelector.selectQualifiedNetwork(false, - mUntrustedConnectionAllowed, mStateMachine.isLinkDebouncing(), + mNetworkSelector.selectNetwork(scanDetails, mStateMachine.isConnected(), mStateMachine.isDisconnected(), - mStateMachine.isSupplicantTransientState(), scanDetails); + mUntrustedConnectionAllowed); mWifiLastResortWatchdog.updateAvailableNetworks( - mQualifiedNetworkSelector.getFilteredScanDetails()); + mNetworkSelector.getFilteredScanDetails()); mWifiMetrics.countScanResults(scanDetails); if (candidate != null) { - localLog(listenerName + ": QNS candidate-" + candidate.SSID); + localLog(listenerName + ": WNS candidate-" + candidate.SSID); connectToNetwork(candidate); return true; } else { @@ -238,63 +242,6 @@ public class WifiConnectivityManager { } } - // Periodic scan results listener. A periodic scan is initiated when - // screen is on. - private class PeriodicScanListener implements WifiScanner.ScanListener { - private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); - - public void clearScanDetails() { - mScanDetails.clear(); - } - - @Override - public void onSuccess() { - localLog("PeriodicScanListener onSuccess"); - } - - @Override - public void onFailure(int reason, String description) { - Log.e(TAG, "PeriodicScanListener onFailure:" - + " reason: " + reason - + " description: " + description); - - // reschedule the scan - if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { - scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS); - } else { - mScanRestartCount = 0; - Log.e(TAG, "Failed to successfully start periodic scan for " - + MAX_SCAN_RESTART_ALLOWED + " times"); - } - } - - @Override - public void onPeriodChanged(int periodInMs) { - localLog("PeriodicScanListener onPeriodChanged: " - + "actual scan period " + periodInMs + "ms"); - } - - @Override - public void onResults(WifiScanner.ScanData[] results) { - handleScanResults(mScanDetails, "PeriodicScanListener"); - clearScanDetails(); - mScanRestartCount = 0; - } - - @Override - public void onFullResult(ScanResult fullScanResult) { - if (mDbg) { - localLog("PeriodicScanListener onFullResult: " - + fullScanResult.SSID + " capabilities " - + fullScanResult.capabilities); - } - - mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult)); - } - } - - private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener(); - // All single scan results listener. // // Note: This is the listener for all the available single scan results, @@ -365,7 +312,7 @@ public class WifiConnectivityManager { private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); // Single scan results listener. A single scan is initiated when - // Disconnected/ConnectedPNO scan found a valid network and woke up + // DisconnectedPNO scan found a valid network and woke up // the system, or by the watchdog timer, or to form the timer based // periodic scan. // @@ -414,9 +361,6 @@ public class WifiConnectivityManager { } } - // re-enable this when b/27695292 is fixed - // private final SingleScanListener mSingleScanListener = new SingleScanListener(); - // PNO scan results listener for both disconected and connected PNO scanning. // A PNO scan is initiated when screen is off. private class PnoScanListener implements WifiScanner.PnoScanListener { @@ -429,7 +373,7 @@ public class WifiConnectivityManager { } // Reset to the start value when either a non-PNO scan is started or - // QNS selects a candidate from the PNO scan results. + // WifiNetworkSelector selects a candidate from the PNO scan results. public void resetLowRssiNetworkRetryDelay() { mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; } @@ -467,7 +411,7 @@ public class WifiConnectivityManager { } // Currently the PNO scan results doesn't include IE, - // which contains information required by QNS. Ignore them + // which contains information required by WifiNetworkSelector. Ignore them // for now. @Override public void onResults(WifiScanner.ScanData[] results) { @@ -491,7 +435,7 @@ public class WifiConnectivityManager { mScanRestartCount = 0; if (!wasConnectAttempted) { - // The scan results were rejected by QNS due to low RSSI values + // The scan results were rejected by WifiNetworkSelector due to low RSSI values if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) { mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS; } @@ -510,15 +454,16 @@ public class WifiConnectivityManager { /** * WifiConnectivityManager constructor */ - public WifiConnectivityManager(Context context, WifiStateMachine stateMachine, + WifiConnectivityManager(Context context, WifiStateMachine stateMachine, WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, - WifiQualifiedNetworkSelector qualifiedNetworkSelector, - WifiInjector wifiInjector, Looper looper, boolean enable) { + WifiNetworkSelector networkSelector, WifiInjector wifiInjector, Looper looper, + boolean enable) { mStateMachine = stateMachine; mScanner = scanner; mConfigManager = configManager; mWifiInfo = wifiInfo; - mQualifiedNetworkSelector = qualifiedNetworkSelector; + mNetworkSelector = networkSelector; + mLocalLog = networkSelector.getLocalLog(); mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog(); mWifiMetrics = wifiInjector.getWifiMetrics(); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); @@ -526,10 +471,12 @@ public class WifiConnectivityManager { mClock = wifiInjector.getClock(); mConnectionAttemptTimeStamps = new LinkedList<>(); - mMin5GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI; - mMin24GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI; - mBand5GHzBonus = WifiQualifiedNetworkSelector.BAND_AWARD_5GHz; - + mMin5GHzRssi = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + mMin24GHzRssi = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + mBand5GHzBonus = context.getResources().getInteger( + R.integer.config_wifi_framework_5GHz_preference_boost_factor); mCurrentConnectionBonus = context.getResources().getInteger( R.integer.config_wifi_framework_current_network_boost); mSameNetworkBonus = context.getResources().getInteger( @@ -538,11 +485,14 @@ public class WifiConnectivityManager { R.integer.config_wifi_framework_SECURITY_AWARD); int thresholdSaturatedRssi24 = context.getResources().getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); - mInitialScoreMax = - (thresholdSaturatedRssi24 + WifiQualifiedNetworkSelector.RSSI_SCORE_OFFSET) - * WifiQualifiedNetworkSelector.RSSI_SCORE_SLOPE; mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( R.bool.config_wifi_framework_enable_associated_network_selection); + mInitialScoreMax = (context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz) + + context.getResources().getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)) + * context.getResources().getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); Log.i(TAG, "PNO settings:" + " min5GHzRssi " + mMin5GHzRssi + " min24GHzRssi " + mMin24GHzRssi @@ -551,6 +501,17 @@ public class WifiConnectivityManager { + " secureNetworkBonus " + mSecureBonus + " initialScoreMax " + mInitialScoreMax); + // Register the network evaluators + SavedNetworkEvaluator savedNetworkEvaluator = new SavedNetworkEvaluator(context, + mConfigManager, mClock, mLocalLog); + mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator, + SAVED_NETWORK_EVALUATOR_PRIORITY); + + ExternalScoreEvaluator externalScoreEvaluator = new ExternalScoreEvaluator(context, + mConfigManager, mClock, mLocalLog); + mNetworkSelector.registerNetworkEvaluator(externalScoreEvaluator, + EXTERNAL_SCORE_EVALUATOR_PRIORITY); + // Register for all single scan results mScanner.registerScanListener(mAllSingleScanListener); @@ -601,7 +562,7 @@ public class WifiConnectivityManager { * Attempt to connect to a network candidate. * * Based on the currently connected network, this menthod determines whether we should - * connect or roam to the network candidate recommended by QNS. + * connect or roam to the network candidate recommended by WifiNetworkSelector. */ private void connectToNetwork(WifiConfiguration candidate) { ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); @@ -616,8 +577,8 @@ public class WifiConnectivityManager { // Check if we are already connected or in the process of connecting to the target // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just - // in case the firmware automatically roamed to a BSSID different from what QNS - // selected. + // in case the firmware automatically roamed to a BSSID different from what + // WifiNetworkSelector selected. if (targetBssid != null && (targetBssid.equals(mLastConnectionAttemptBssid) || targetBssid.equals(mWifiInfo.getBSSID())) @@ -703,7 +664,7 @@ public class WifiConnectivityManager { // Otherwise, the watchdog timer will be scheduled when entering disconnected // state. if (mWifiState == WIFI_STATE_DISCONNECTED) { - Log.i(TAG, "start a single scan from watchdogHandler"); + localLog("start a single scan from watchdogHandler"); scheduleWatchdogTimer(); startSingleScan(true); @@ -787,9 +748,6 @@ public class WifiConnectivityManager { settings.hiddenNetworks = hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]); - // re-enable this when b/27695292 is fixed - // mSingleScanListener.clearScanDetails(); - // mScanner.startScan(settings, mSingleScanListener, WIFI_WORK_SOURCE); SingleScanListener singleScanListener = new SingleScanListener(isFullBandScan); mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); @@ -806,23 +764,11 @@ public class WifiConnectivityManager { // Due to b/28020168, timer based single scan will be scheduled // to provide periodic scan in an exponential backoff fashion. - if (!ENABLE_BACKGROUND_SCAN) { - if (scanImmediately) { - resetLastPeriodicSingleScanTimeStamp(); - } - mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; - startPeriodicSingleScan(); - } else { - ScanSettings settings = new ScanSettings(); - settings.band = getScanBand(); - settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT - | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; - settings.numBssidsPerScan = 0; - settings.periodInMs = PERIODIC_SCAN_INTERVAL_MS; - - mPeriodicScanListener.clearScanDetails(); - mScanner.startBackgroundScan(settings, mPeriodicScanListener, WIFI_WORK_SOURCE); + if (scanImmediately) { + resetLastPeriodicSingleScanTimeStamp(); } + mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; + startPeriodicSingleScan(); } // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected @@ -856,8 +802,6 @@ public class WifiConnectivityManager { scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; scanSettings.numBssidsPerScan = 0; scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS; - // TODO: enable exponential back off scan later to further save energy - // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs; mPnoScanListener.clearScanDetails(); @@ -865,51 +809,7 @@ public class WifiConnectivityManager { mPnoScanStarted = true; } - // Start a ConnectedPNO scan when screen is off and Wifi is connected - private void startConnectedPnoScan() { - // TODO(b/29503772): Need to change this interface. - // Disable ConnectedPNO for now due to b/28020168 - if (!ENABLE_CONNECTED_PNO_SCAN) { - return; - } - - // Initialize PNO settings - PnoSettings pnoSettings = new PnoSettings(); - List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList(); - int listSize = pnoNetworkList.size(); - - if (listSize == 0) { - // No saved network - localLog("No saved network for starting connected PNO."); - return; - } - - pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; - pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); - pnoSettings.min5GHzRssi = mMin5GHzRssi; - pnoSettings.min24GHzRssi = mMin24GHzRssi; - pnoSettings.initialScoreMax = mInitialScoreMax; - pnoSettings.currentConnectionBonus = mCurrentConnectionBonus; - pnoSettings.sameNetworkBonus = mSameNetworkBonus; - pnoSettings.secureBonus = mSecureBonus; - pnoSettings.band5GHzBonus = mBand5GHzBonus; - - // Initialize scan settings - ScanSettings scanSettings = new ScanSettings(); - scanSettings.band = getScanBand(); - scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; - scanSettings.numBssidsPerScan = 0; - scanSettings.periodInMs = CONNECTED_PNO_SCAN_INTERVAL_MS; - // TODO: enable exponential back off scan later to further save energy - // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs; - - mPnoScanListener.clearScanDetails(); - - mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); - mPnoScanStarted = true; - } - - // Stop a PNO scan. This includes both DisconnectedPNO and ConnectedPNO scans. + // Stop PNO scan. private void stopPnoScan() { if (mPnoScanStarted) { mScanner.stopPnoScan(mPnoScanListener); @@ -920,7 +820,7 @@ public class WifiConnectivityManager { // Set up watchdog timer private void scheduleWatchdogTimer() { - Log.i(TAG, "scheduleWatchdogTimer"); + localLog("scheduleWatchdogTimer"); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS, @@ -994,9 +894,7 @@ public class WifiConnectivityManager { if (mScreenOn) { startPeriodicScan(scanImmediately); } else { // screenOff - if (mWifiState == WIFI_STATE_CONNECTED) { - startConnectedPnoScan(); - } else { + if (mWifiState == WIFI_STATE_DISCONNECTED) { startDisconnectedPnoScan(); } } @@ -1006,11 +904,7 @@ public class WifiConnectivityManager { private void stopConnectivityScan() { // Due to b/28020168, timer based single scan will be scheduled // to provide periodic scan in an exponential backoff fashion. - if (!ENABLE_BACKGROUND_SCAN) { - cancelPeriodicScanTimer(); - } else { - mScanner.stopBackgroundScan(mPeriodicScanListener); - } + cancelPeriodicScanTimer(); stopPnoScan(); mScanRestartCount = 0; } @@ -1048,7 +942,7 @@ public class WifiConnectivityManager { * Handler when user toggles whether untrusted connection is allowed */ public void setUntrustedConnectionAllowed(boolean allowed) { - Log.i(TAG, "setUntrustedConnectionAllowed: allowed=" + allowed); + localLog("setUntrustedConnectionAllowed: allowed=" + allowed); if (mUntrustedConnectionAllowed != allowed) { mUntrustedConnectionAllowed = allowed; @@ -1060,10 +954,9 @@ public class WifiConnectivityManager { * Handler when user specifies a particular network to connect to */ public void setUserConnectChoice(int netId) { - Log.i(TAG, "setUserConnectChoice: netId=" + netId); - - mQualifiedNetworkSelector.setUserConnectChoice(netId); + localLog("setUserConnectChoice: netId=" + netId); + mNetworkSelector.setUserConnectChoice(netId); clearConnectionAttemptTimeStamps(); } @@ -1071,24 +964,24 @@ public class WifiConnectivityManager { * Handler for on-demand connectivity scan */ public void forceConnectivityScan() { - Log.i(TAG, "forceConnectivityScan"); + localLog("forceConnectivityScan"); startConnectivityScan(SCAN_IMMEDIATELY); } /** - * Track whether a BSSID should be enabled or disabled for QNS + * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector */ public boolean trackBssid(String bssid, boolean enable) { - Log.i(TAG, "trackBssid: " + (enable ? "enable " : "disable ") + bssid); + localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid); - boolean ret = mQualifiedNetworkSelector - .enableBssidForQualityNetworkSelection(bssid, enable); + boolean ret = mNetworkSelector + .enableBssidForNetworkSelection(bssid, enable); if (ret && !enable) { // Disabling a BSSID can happen when the AP candidate to connect to has - // no capacity for new stations. We start another scan immediately so that QNS - // can give us another candidate to connect to. + // no capacity for new stations. We start another scan immediately so that + // WifiNetworkSelector can give us another candidate to connect to. startConnectivityScan(SCAN_IMMEDIATELY); } @@ -1099,7 +992,7 @@ public class WifiConnectivityManager { * Inform WiFi is enabled for connection or not */ public void setWifiEnabled(boolean enable) { - Log.i(TAG, "Set WiFi " + (enable ? "enabled" : "disabled")); + localLog("Set WiFi " + (enable ? "enabled" : "disabled")); mWifiEnabled = enable; @@ -1108,7 +1001,7 @@ public class WifiConnectivityManager { resetLastPeriodicSingleScanTimeStamp(); mLastConnectionAttemptBssid = null; } else if (mWifiConnectivityManagerEnabled) { - startConnectivityScan(SCAN_IMMEDIATELY); + startConnectivityScan(SCAN_IMMEDIATELY); } } @@ -1116,7 +1009,7 @@ public class WifiConnectivityManager { * Turn on/off the WifiConnectivityMangager at runtime */ public void enable(boolean enable) { - Log.i(TAG, "Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled")); + localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled")); mWifiConnectivityManagerEnabled = enable; @@ -1125,29 +1018,10 @@ public class WifiConnectivityManager { resetLastPeriodicSingleScanTimeStamp(); mLastConnectionAttemptBssid = null; } else if (mWifiEnabled) { - startConnectivityScan(SCAN_IMMEDIATELY); + startConnectivityScan(SCAN_IMMEDIATELY); } } - /** - * Enable/disable verbose logging - */ - public void enableVerboseLogging(int verbose) { - mDbg = verbose > 0; - } - - /** - * Dump the local log buffer - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Dump of WifiConnectivityManager"); - pw.println("WifiConnectivityManager - Log Begin ----"); - pw.println("WifiConnectivityManager - Number of connectivity attempts rate limited: " - + mTotalConnectivityAttemptsRateLimited); - mLocalLog.dump(fd, pw, args); - pw.println("WifiConnectivityManager - Log End ----"); - } - @VisibleForTesting int getLowRssiNetworkRetryDelay() { return mPnoScanListener.getLowRssiNetworkRetryDelay(); diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java new file mode 100644 index 000000000..fec10eec7 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -0,0 +1,589 @@ +/* + * 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.Nullable; +import android.app.ActivityManager; +import android.content.Context; +import android.net.NetworkKey; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.text.TextUtils; +import android.util.LocalLog; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This class looks at all the connectivity scan results then + * selects a network for the phone to connect or roam to. + */ +public class WifiNetworkSelector { + private static final String TAG = "WifiNetworkSelector"; + private static final long INVALID_TIME_STAMP = Long.MIN_VALUE; + // Minimum time gap between last successful network selection and a new selection + // attempt. + @VisibleForTesting + public static final int MINIMUM_NETWORK_SELECTION_INTERVAL_MS = 10 * 1000; + + // Constants for BSSID blacklist. + public static final int BSSID_BLACKLIST_THRESHOLD = 3; + public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000; + + private WifiConfigManager mWifiConfigManager; + private WifiInfo mWifiInfo; + private Clock mClock; + private WifiConfiguration mCurrentNetwork = null; + private String mCurrentBssid = null; + private static class BssidBlacklistStatus { + // Number of times this BSSID has been rejected for association. + public int counter; + public boolean isBlacklisted; + public long blacklistedTimeStamp = INVALID_TIME_STAMP; + } + private Map<String, BssidBlacklistStatus> mBssidBlacklist = + new HashMap<>(); + + private final LocalLog mLocalLog = + new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512); + private long mLastNetworkSelectionTimeStamp = INVALID_TIME_STAMP; + // Buffer of filtered scan results (Scan results considered by network selection) & associated + // WifiConfiguration (if any). + private volatile List<Pair<ScanDetail, WifiConfiguration>> mConnectableNetworks = + new ArrayList<>(); + private final int mThresholdQualifiedRssi24; + private final int mThresholdQualifiedRssi5; + private final int mThresholdMinimumRssi24; + private final int mThresholdMinimumRssi5; + private final boolean mEnableAutoJoinWhenAssociated; + + /** + * WiFi Network Selector supports various types of networks. Each type can + * have its evaluator to choose the best WiFi network for the device to connect + * to. When registering a WiFi network evaluator with the WiFi Network Selector, + * the priority of the network must be specified, and it must be a value between + * 0 and (EVALUATOR_MIN_PIRORITY - 1) with 0 being the highest priority. Wifi + * Network Selector iterates through the registered scorers from the highest priority + * to the lowest till a network is selected. + */ + public static final int EVALUATOR_MIN_PRIORITY = 6; + + /** + * Maximum number of evaluators can be registered with Wifi Network Selector. + */ + public static final int MAX_NUM_EVALUATORS = EVALUATOR_MIN_PRIORITY; + + /** + * Interface for WiFi Network Evaluator + * + * A network scorer evaulates all the networks from the scan results and + * recommends the best network in its category to connect or roam to. + */ + public interface NetworkEvaluator { + /** + * Get the evaluator name. + */ + String getName(); + + /** + * Update the evaluator. + * + * Certain evaluators have to be updated with the new scan results. For example + * the ExternalScoreEvalutor needs to refresh its Score Cache. + * + * @param scanDetails a list of scan details constructed from the scan results + */ + void update(List<ScanDetail> scanDetails); + + /** + * Evaluate all the networks from the scan results. + * + * @param scanDetails a list of scan details constructed from the scan results + * @param currentNetwork configuration of the current connected network + * or null if disconnected + * @param currentBssid BSSID of the current connected network or null if + * disconnected + * @param connected a flag to indicate if WifiStateMachine is in connected + * state + * @param untrustedNetworkAllowed a flag to indidate if untrusted networks like + * ephemeral networks are allowed + * @param connectableNetworks a list of the ScanDetail and WifiConfiguration + * pair which is used by the WifiLastResortWatchdog + * @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, + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks); + } + + private final NetworkEvaluator[] mEvaluators = new NetworkEvaluator[MAX_NUM_EVALUATORS]; + + // A helper to log debugging information in the local log buffer, which can + // be retrieved in bugreport. + private void localLog(String log) { + mLocalLog.log(log); + } + + private boolean isCurrentNetworkSufficient(WifiConfiguration network) { + // Currently connected? + if (network == null) { + localLog("No current connected network."); + return false; + } else { + localLog("Current connected network: " + network.SSID + + " , ID: " + network.networkId); + } + + // Ephemeral network is not qualified. + if (network.ephemeral) { + localLog("Current network is an ephemeral one."); + return false; + } + + // Open network is not qualified. + if (WifiConfigurationUtil.isConfigForOpenNetwork(network)) { + localLog("Current network is a open one."); + return false; + } + + // 2.4GHz networks is not qualified. + if (mWifiInfo.is24GHz()) { + localLog("Current network is 2.4GHz."); + return false; + } + + // Is the current network's singnal strength qualified? It can only + // be a 5GHz network if we reach here. + int currentRssi = mWifiInfo.getRssi(); + if (mWifiInfo.is5GHz() && currentRssi < mThresholdQualifiedRssi5) { + localLog("Current network band=" + (mWifiInfo.is5GHz() ? "5GHz" : "2.4GHz") + + ", RSSI[" + currentRssi + "]-acceptable but not qualified."); + return false; + } + + return true; + } + + private boolean isNetworkSelectionNeeded(List<ScanDetail> scanDetails, + boolean connected, boolean disconnected) { + if (scanDetails.size() == 0) { + localLog("Empty connectivity scan results. Skip network selection."); + return false; + } + + if (connected) { + // Is roaming allowed? + if (!mEnableAutoJoinWhenAssociated) { + localLog("Switching networks in connected state is not allowed." + + " Skip network selection."); + return false; + } + + // Has it been at least the minimum interval since last network selection? + if (mLastNetworkSelectionTimeStamp != INVALID_TIME_STAMP) { + long gap = mClock.getElapsedSinceBootMillis() + - mLastNetworkSelectionTimeStamp; + if (gap < MINIMUM_NETWORK_SELECTION_INTERVAL_MS) { + localLog("Too short since last network selection: " + gap + " ms." + + " Skip network selection."); + return false; + } + } + + if (isCurrentNetworkSufficient(mCurrentNetwork)) { + localLog("Current connected network already sufficient. Skip network selection."); + return false; + } else { + localLog("Current connected network is not sufficient."); + } + } else if (disconnected) { + mCurrentNetwork = null; + mCurrentBssid = null; + } else { + // No network selection if WifiStateMachine is in a state other than + // CONNECTED or DISCONNECTED. + localLog("WifiStateMachine is in neither CONNECTED nor DISCONNECTED state." + + " Skip network selection."); + return false; + } + + return true; + } + + /** + * Format the given ScanResult as a scan ID for logging. + */ + public static String toScanId(@Nullable ScanResult scanResult) { + return scanResult == null ? "NULL" + : String.format("%s:%s", scanResult.SSID, scanResult.BSSID); + } + + /** + * Format the given WifiConfiguration as a SSID:netId string + */ + public static String toNetworkString(WifiConfiguration network) { + if (network == null) { + return null; + } + + return (network.SSID + ":" + network.networkId); + } + + private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails) { + ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>(); + List<ScanDetail> validScanDetails = new ArrayList<ScanDetail>(); + StringBuffer noValidSsid = new StringBuffer(); + StringBuffer blacklistedBssid = new StringBuffer(); + StringBuffer lowRssi = new StringBuffer(); + + for (ScanDetail scanDetail : scanDetails) { + ScanResult scanResult = scanDetail.getScanResult(); + + if (TextUtils.isEmpty(scanResult.SSID)) { + noValidSsid.append(scanResult.BSSID).append(" / "); + continue; + } + + final String scanId = toScanId(scanResult); + + if (isBssidDisabled(scanResult.BSSID)) { + blacklistedBssid.append(scanId).append(" / "); + continue; + } + + // Skip network with too weak signals. + if ((scanResult.is24GHz() && scanResult.level + < mThresholdMinimumRssi24) + || (scanResult.is5GHz() && scanResult.level + < mThresholdMinimumRssi5)) { + lowRssi.append(scanId).append("(") + .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz") + .append(")").append(scanResult.level).append(" / "); + continue; + } + + validScanDetails.add(scanDetail); + } + + if (noValidSsid.length() != 0) { + localLog("Networks filtered out due to invalid SSID: " + noValidSsid); + } + + if (blacklistedBssid.length() != 0) { + localLog("Networks filtered out due to blacklist: " + blacklistedBssid); + } + + if (lowRssi.length() != 0) { + localLog("Networks filtered out due to low signal strength: " + lowRssi); + } + + return validScanDetails; + } + + /** + * @return the list of ScanDetails scored as potential candidates by the last run of + * selectNetwork, this will be empty if Network selector determined no selection was + * needed on last run. This includes scan details of sufficient signal strength, and + * had an associated WifiConfiguration. + */ + public List<Pair<ScanDetail, WifiConfiguration>> getFilteredScanDetails() { + return mConnectableNetworks; + } + + /** + * This API is called when user explicitly selects a network. Currently, it is used in following + * cases: + * (1) User explicitly chooses to connect to a saved network. + * (2) User saves a network after adding a new network. + * (3) User saves a network after modifying a saved network. + * Following actions will be triggered: + * 1. If this network is disabled, we need re-enable it again. + * 2. This network is favored over all the other networks visible in latest network + * selection procedure. + * + * @param netId ID for the network chosen by the user + * @return true -- There is change made to connection choice of any saved network. + * false -- There is no change made to connection choice of any saved network. + */ + public boolean setUserConnectChoice(int netId) { + localLog("userSelectNetwork: network ID=" + netId); + WifiConfiguration selected = mWifiConfigManager.getConfiguredNetwork(netId); + + if (selected == null || selected.SSID == null) { + localLog("userSelectNetwork: Invalid configuration with nid=" + netId); + return false; + } + + // Enable the network if it is disabled. + if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) { + mWifiConfigManager.updateNetworkSelectionStatus(netId, + WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); + } + + boolean change = false; + String key = selected.configKey(); + // This is only used for setting the connect choice timestamp for debugging purposes. + long currentTime = mClock.getWallClockMillis(); + List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks(); + + for (WifiConfiguration network : savedNetworks) { + WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); + if (network.networkId == selected.networkId) { + if (status.getConnectChoice() != null) { + localLog("Remove user selection preference of " + status.getConnectChoice() + + " Set Time: " + status.getConnectChoiceTimestamp() + " from " + + network.SSID + " : " + network.networkId); + mWifiConfigManager.clearNetworkConnectChoice(network.networkId); + change = true; + } + continue; + } + + if (status.getSeenInLastQualifiedNetworkSelection() + && (status.getConnectChoice() == null + || !status.getConnectChoice().equals(key))) { + localLog("Add key: " + key + " Set Time: " + currentTime + " to " + + toNetworkString(network)); + mWifiConfigManager.setNetworkConnectChoice(network.networkId, key, currentTime); + change = true; + } + } + + return change; + } + + /** + * Enable/disable a BSSID for Network Selection + * When an association rejection event is obtained, Network Selector will disable this + * BSSID but supplicant still can try to connect to this bssid. If supplicant connect to it + * successfully later, this bssid can be re-enabled. + * + * @param bssid the bssid to be enabled / disabled + * @param enable -- true enable a bssid if it has been disabled + * -- false disable a bssid + */ + public boolean enableBssidForNetworkSelection(String bssid, boolean enable) { + if (enable) { + return (mBssidBlacklist.remove(bssid) != null); + } else { + if (bssid != null) { + BssidBlacklistStatus status = mBssidBlacklist.get(bssid); + if (status == null) { + // First time for this BSSID + BssidBlacklistStatus newStatus = new BssidBlacklistStatus(); + newStatus.counter++; + mBssidBlacklist.put(bssid, newStatus); + } else if (!status.isBlacklisted) { + status.counter++; + if (status.counter >= BSSID_BLACKLIST_THRESHOLD) { + status.isBlacklisted = true; + status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis(); + return true; + } + } + } + } + return false; + } + + /** + * Update the BSSID blacklist + * + * Go through the BSSID blacklist and check when a BSSID was blocked. If it + * has been blacklisted for BSSID_BLACKLIST_EXPIRE_TIME_MS, then re-enable it. + */ + private void updateBssidBlacklist() { + Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator(); + while (iter.hasNext()) { + BssidBlacklistStatus status = iter.next(); + if (status != null && status.isBlacklisted) { + if (mClock.getElapsedSinceBootMillis() - status.blacklistedTimeStamp + >= BSSID_BLACKLIST_EXPIRE_TIME_MS) { + iter.remove(); + } + } + } + } + + /** + * Check whether a bssid is disabled + * @param bssid -- the bssid to check + */ + private boolean isBssidDisabled(String bssid) { + BssidBlacklistStatus status = mBssidBlacklist.get(bssid); + return status == null ? false : status.isBlacklisted; + } + + /** + * + */ + @Nullable + public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails, + boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) { + mConnectableNetworks.clear(); + if (scanDetails.size() == 0) { + localLog("Empty connectivity scan result"); + return null; + } + + if (mCurrentNetwork == null) { + mCurrentNetwork = + mWifiConfigManager.getConfiguredNetwork(mWifiInfo.getNetworkId()); + } + + // Always get the current BSSID from WifiInfo in case that firmware initiated + // roaming happened. + mCurrentBssid = mWifiInfo.getBSSID(); + + // Shall we start network selection at all? + if (!isNetworkSelectionNeeded(scanDetails, connected, disconnected)) { + return null; + } + + // Update the registered network evaluators. + for (NetworkEvaluator registeredEvaluator : mEvaluators) { + if (registeredEvaluator != null) { + registeredEvaluator.update(scanDetails); + } + } + + // Check if any BSSID can be freed from the blacklist. + updateBssidBlacklist(); + + // Filter out unwanted networks. + List<ScanDetail> filteredScanDetails = filterScanResults(scanDetails); + if (filteredScanDetails.size() == 0) { + return null; + } + + // Go through the registered network evaluators from the highest priority + // one to the lowest till a network is selected. + WifiConfiguration selectedNetwork = null; + for (NetworkEvaluator registeredEvaluator : mEvaluators) { + if (registeredEvaluator != null) { + selectedNetwork = registeredEvaluator.evaluateNetworks(scanDetails, + mCurrentNetwork, mCurrentBssid, connected, + untrustedNetworkAllowed, mConnectableNetworks); + if (selectedNetwork != null) { + break; + } + } + } + + if (selectedNetwork != null) { + mCurrentNetwork = selectedNetwork; + mCurrentBssid = selectedNetwork.getNetworkSelectionStatus().getCandidate().BSSID; + mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); + } + + return selectedNetwork; + } + + /** + * Register a network evaluator + * + * @param evaluator the network evaluator to be registered + * @param priority a value between 0 and (SCORER_MIN_PRIORITY-1) + * + * @return true if the evaluator is successfully registered with QNS; + * false if failed to register the evaluator + */ + public boolean registerNetworkEvaluator(NetworkEvaluator evaluator, int priority) { + if (priority < 0 || priority >= EVALUATOR_MIN_PRIORITY) { + Log.e(TAG, "Invalid network evaluator priority: " + priority); + return false; + } + + if (mEvaluators[priority] != null) { + Log.e(TAG, "Priority " + priority + " is already registered by " + + mEvaluators[priority].getName()); + return false; + } + + mEvaluators[priority] = evaluator; + return true; + } + + /** + * Unregister a network evaluator + * + * @param evaluator the network evaluator to be unregistered from QNS + * + * @return true if the evaluator is successfully unregistered from; + * false if failed to unregister the evaluator + */ + public boolean unregisterNetworkEvaluator(NetworkEvaluator evaluator) { + for (NetworkEvaluator registeredEvaluator : mEvaluators) { + if (registeredEvaluator == evaluator) { + Log.d(TAG, "Unregistered network evaluator: " + evaluator.getName()); + return true; + } + } + + Log.e(TAG, "Couldn't unregister network evaluator: " + evaluator.getName()); + return false; + } + + WifiNetworkSelector(Context context, WifiConfigManager configManager, + WifiInfo wifiInfo, Clock clock) { + mWifiConfigManager = configManager; + mWifiInfo = wifiInfo; + mClock = clock; + + mThresholdQualifiedRssi24 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); + mThresholdQualifiedRssi5 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); + mThresholdMinimumRssi24 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + mThresholdMinimumRssi5 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( + R.bool.config_wifi_framework_enable_associated_network_selection); + } + + /** + * Retrieve the local log buffer created by WifiNetworkSelector. + */ + public LocalLog getLocalLog() { + return mLocalLog; + } + + /** + * Dump the local logs. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Dump of WifiNetworkSelector"); + pw.println("WifiNetworkSelector - Log Begin ----"); + mLocalLog.dump(fd, pw, args); + pw.println("WifiNetworkSelector - Log End ----"); + } +} diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java deleted file mode 100644 index d920273f4..000000000 --- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java +++ /dev/null @@ -1,1062 +0,0 @@ -/* - * Copyright (C) 2015 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.Nullable; -import android.content.Context; -import android.net.NetworkKey; -import android.net.NetworkScoreManager; -import android.net.WifiKey; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Process; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.wifi.util.ScanResultUtil; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * This class looks at all the connectivity scan results then - * selects a network for the phone to connect or roam to. - */ -public class WifiQualifiedNetworkSelector { - private WifiConfigManager mWifiConfigManager; - private WifiInfo mWifiInfo; - private NetworkScoreManager mScoreManager; - private WifiNetworkScoreCache mNetworkScoreCache; - private Clock mClock; - private static final String TAG = "WifiQualifiedNetworkSelector:"; - // Always enable debugging logs for now since QNS is still a new feature. - private static final boolean FORCE_DEBUG = true; - private boolean mDbg = FORCE_DEBUG; - private WifiConfiguration mCurrentConnectedNetwork = null; - private String mCurrentBssid = null; - - // Buffer of filtered scan results (Scan results considered by network selection) & associated - // WifiConfiguration (if any). - private volatile List<Pair<ScanDetail, WifiConfiguration>> mFilteredScanDetails = null; - - // Minimum time gap between last successful Qualified Network Selection and a new selection - // attempt. - private static final int MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS = 10 * 1000; - - // A 2.4GHz network with RSSI value above this threshold is considered qualified. No new - // selection attempt necessary. - public static final int QUALIFIED_RSSI_24G_BAND = -73; - // A 5GHz network with RSSI value above this threshold is considered qualified. No new - // selection attempt necessary. - public static final int QUALIFIED_RSSI_5G_BAND = -70; - // A RSSI vaule larger than this threshold is considered saturated and switching to a - // higher RSSI value network won't benefit the connection much. - public static final int RSSI_SATURATION_2G_BAND = -60; - public static final int RSSI_SATURATION_5G_BAND = -57; - // Any RSSI value below this is considered unacceptable, and the network will be filtered out. - public static final int MINIMUM_2G_ACCEPT_RSSI = -85; - public static final int MINIMUM_5G_ACCEPT_RSSI = -82; - - // Constants for BSSID scoring formula. - public static final int RSSI_SCORE_SLOPE = 4; - public static final int RSSI_SCORE_OFFSET = 85; - public static final int BAND_AWARD_5GHz = 40; - public static final int SAME_NETWORK_AWARD = 16; - public static final int SAME_BSSID_AWARD = 24; - public static final int LAST_SELECTION_AWARD = 480; - public static final int PASSPOINT_SECURITY_AWARD = 40; - public static final int SECURITY_AWARD = 80; - - // BSSID blacklist parameters. - public static final int BSSID_BLACKLIST_THRESHOLD = 3; - public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000; - - private final int mNoIntnetPenalty; - private static final int INVALID_TIME_STAMP = -1; - private long mLastQualifiedNetworkSelectionTimeStamp = INVALID_TIME_STAMP; - - private final LocalLog mLocalLog = new LocalLog(512); - private int mRssiScoreSlope = RSSI_SCORE_SLOPE; - private int mRssiScoreOffset = RSSI_SCORE_OFFSET; - private int mSameBssidAward = SAME_BSSID_AWARD; - private int mLastSelectionAward = LAST_SELECTION_AWARD; - private int mPasspointSecurityAward = PASSPOINT_SECURITY_AWARD; - private int mSecurityAward = SECURITY_AWARD; - private final int mThresholdQualifiedRssi24; - private final int mThresholdQualifiedRssi5; - private final boolean mEnableAutoJoinWhenAssociated; - private final int mBandAward5Ghz; - private final int mCurrentNetworkBoost; - private final int mThresholdSaturatedRssi24; - private final int mThresholdSaturatedRssi5; - private final int mThresholdMinimumRssi5; - private final int mThresholdMinimumRssi24; - private int mUserPreferedBand = WifiManager.WIFI_FREQUENCY_BAND_AUTO; - private Map<String, BssidBlacklistStatus> mBssidBlacklist = - new HashMap<String, BssidBlacklistStatus>(); - - /** - * Class that saves the blacklist status of a given BSSID. - */ - private static class BssidBlacklistStatus { - // Number of times this BSSID has been requested to be blacklisted. - // Association rejection triggers such a request. - int mCounter; - boolean mIsBlacklisted; - long mBlacklistedTimeStamp = INVALID_TIME_STAMP; - } - - private void localLog(String log) { - if (mDbg) { - mLocalLog.log(log); - } - } - - private void localLoge(String log) { - mLocalLog.log(log); - } - - @VisibleForTesting - void setWifiNetworkScoreCache(WifiNetworkScoreCache cache) { - mNetworkScoreCache = cache; - } - - /** - * @return current target connected network - */ - public WifiConfiguration getConnetionTargetNetwork() { - return mCurrentConnectedNetwork; - } - - /** - * @return the list of ScanDetails scored as potential candidates by the last run of - * selectQualifiedNetwork, this will be empty if QNS determined no selection was needed on last - * run. This includes scan details of sufficient signal strength, and had an associated - * WifiConfiguration. - */ - public List<Pair<ScanDetail, WifiConfiguration>> getFilteredScanDetails() { - return mFilteredScanDetails; - } - - WifiQualifiedNetworkSelector(WifiConfigManager configManager, Context context, - WifiInfo wifiInfo, Clock clock) { - mWifiConfigManager = configManager; - mWifiInfo = wifiInfo; - mClock = clock; - mScoreManager = - (NetworkScoreManager) context.getSystemService(Context.NETWORK_SCORE_SERVICE); - if (mScoreManager != null) { - mNetworkScoreCache = new WifiNetworkScoreCache(context); - mScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache); - } else { - localLoge("Couldn't get NETWORK_SCORE_SERVICE."); - mNetworkScoreCache = null; - } - - mRssiScoreSlope = context.getResources().getInteger( - R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); - mRssiScoreOffset = context.getResources().getInteger( - R.integer.config_wifi_framework_RSSI_SCORE_OFFSET); - mSameBssidAward = context.getResources().getInteger( - R.integer.config_wifi_framework_SAME_BSSID_AWARD); - mLastSelectionAward = context.getResources().getInteger( - R.integer.config_wifi_framework_LAST_SELECTION_AWARD); - mPasspointSecurityAward = context.getResources().getInteger( - R.integer.config_wifi_framework_PASSPOINT_SECURITY_AWARD); - mSecurityAward = context.getResources().getInteger( - R.integer.config_wifi_framework_SECURITY_AWARD); - mThresholdQualifiedRssi24 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); - mThresholdQualifiedRssi5 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); - mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( - R.bool.config_wifi_framework_enable_associated_network_selection); - mBandAward5Ghz = context.getResources().getInteger( - R.integer.config_wifi_framework_5GHz_preference_boost_factor); - mCurrentNetworkBoost = context.getResources().getInteger( - R.integer.config_wifi_framework_current_network_boost); - mThresholdSaturatedRssi24 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); - mThresholdSaturatedRssi5 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz); - mThresholdMinimumRssi5 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); - mThresholdMinimumRssi24 = context.getResources().getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); - mNoIntnetPenalty = - (mThresholdSaturatedRssi24 + mRssiScoreOffset) * mRssiScoreSlope + - mBandAward5Ghz + mCurrentNetworkBoost + mSameBssidAward + mSecurityAward; - } - - void enableVerboseLogging(int verbose) { - mDbg = verbose > 0 || FORCE_DEBUG; - } - - private String getNetworkString(WifiConfiguration network) { - if (network == null) { - return null; - } - - return (network.SSID + ":" + network.networkId); - - } - - /** - * Check if the current connected network is already qualified so that network - * selection from the new scan results is not necessary. - * - * @param currentNetwork -- current connected network - */ - private boolean isCurrentNetworkQualified(WifiConfiguration currentNetwork) { - if (currentNetwork == null) { - localLog("No current connected network"); - return false; - } else { - localLog("Current connected network: " + currentNetwork.SSID - + " , ID: " + currentNetwork.networkId); - } - - // Ephemeral networks are not qualified. - if (currentNetwork.ephemeral) { - localLog("Current network is an ephemeral one"); - return false; - } - - // Open networks are not qualified. - if (WifiConfigurationUtil.isConfigForOpenNetwork(currentNetwork)) { - localLog("Current network is a open one"); - return false; - } - - // Does the current network band match the user preference? - // - // Note, here the check for 2.4GHz band is different from the one for 5GHz band - // such that 5GHz band is always favored. - // When the current network is 2.4GHz, it is considered as not qualified as long - // as the band preference set by user is not 2.4GHz only. This gives QNS an - // opportunity to recommend a 5GHz network if one is available. - // When the current network is 5GHz, it's considered as not qualified only if - // the band preference set by user is 2.4GHz only. - if ((mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ)) - || (mWifiInfo.is5GHz() - && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ))) { - localLog("Current network band does not match user preference: " - + "current network band=" + (mWifiInfo.is24GHz() ? "2.4GHz" : "5GHz") - + ", however user preferred band=" + mUserPreferedBand); - return false; - } - - // Is the current network's singnal strength qualified? - int currentRssi = mWifiInfo.getRssi(); - if ((mWifiInfo.is24GHz() && currentRssi < mThresholdQualifiedRssi24) - || (mWifiInfo.is5GHz() && currentRssi < mThresholdQualifiedRssi5)) { - localLog("Current network band=" + (mWifiInfo.is24GHz() ? "2.4GHz" : "5GHz") - + ", RSSI[" + currentRssi + "]-acceptable but not qualified"); - return false; - } - - return true; - } - - /** - * Check whether QualifiedNetworkSelection is needed. - * - * @param isLinkDebouncing true -- Link layer is under debouncing - * false -- Link layer is not under debouncing - * @param isConnected true -- device is connected to an AP currently - * false -- device is not connected to an AP currently - * @param isDisconnected true -- WifiStateMachine is at disconnected state - * false -- WifiStateMachine is not at disconnected state - * @param isSupplicantTransientState true -- supplicant is in a transient state now - * false -- supplicant is not in a transient state now - */ - private boolean needQualifiedNetworkSelection(boolean isLinkDebouncing, boolean isConnected, - boolean isDisconnected, boolean isSupplicantTransientState) { - // No Qualified Network Selection during the L2 link debouncing procedure. - if (isLinkDebouncing) { - localLog("No QNS during L2 debouncing"); - return false; - } - - if (isConnected) { - // Already connected. Looking for a better candidate. - - // Is network switching allowed in connected state? - if (!mEnableAutoJoinWhenAssociated) { - localLog("Switching networks in connected state is not allowed"); - return false; - } - - // Do not select again if last selection is within - // MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS. - if (mLastQualifiedNetworkSelectionTimeStamp != INVALID_TIME_STAMP) { - long gap = mClock.getElapsedSinceBootMillis() - - mLastQualifiedNetworkSelectionTimeStamp; - if (gap < MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS) { - localLog("Too short from last successful Qualified Network Selection. Gap is:" - + gap + " ms!"); - return false; - } - } - - WifiConfiguration currentNetwork = - mWifiConfigManager.getConfiguredNetwork(mWifiInfo.getNetworkId()); - if (currentNetwork == null) { - // WifiStateMachine in connected state but WifiInfo is not. It means there is a race - // condition. Defer QNS until WifiStateMachine enters the disconnected state. - // - // TODO(b/28249371): Root cause this race condition. - return false; - } - - // Already connected to a qualified network? - if (!isCurrentNetworkQualified(mCurrentConnectedNetwork)) { - localLog("Current connected network is not qualified"); - return true; - } else { - return false; - } - } else if (isDisconnected) { - mCurrentConnectedNetwork = null; - mCurrentBssid = null; - // Defer Qualified Network Selection if wpa_supplicant is in the transient state. - if (isSupplicantTransientState) { - return false; - } - } else { - // Do not allow new network selection if WifiStateMachine is in a state - // other than connected or disconnected. - localLog("WifiStateMachine is not on connected or disconnected state"); - return false; - } - - return true; - } - - int calculateBssidScore(ScanResult scanResult, WifiConfiguration network, - boolean sameNetwork, boolean sameBssid, boolean sameSelect, StringBuffer sbuf) { - - int score = 0; - // Calculate the RSSI score. - int rssi = scanResult.level <= mThresholdSaturatedRssi24 - ? scanResult.level : mThresholdSaturatedRssi24; - score += (rssi + mRssiScoreOffset) * mRssiScoreSlope; - sbuf.append("RSSI score: ").append(score); - - // 5GHz band bonus. - if (scanResult.is5GHz()) { - score += mBandAward5Ghz; - sbuf.append(" 5GHz bonus: ").append(mBandAward5Ghz); - } - - // Last user selection award. - if (sameSelect) { - long timeDifference = - mClock.getElapsedSinceBootMillis() - - mWifiConfigManager.getLastSelectedTimeStamp(); - if (timeDifference > 0) { - int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60); - score += bonus > 0 ? bonus : 0; - sbuf.append(" User selected it last time ").append(timeDifference / 1000 / 60) - .append(" minutes ago, bonus: ").append(bonus); - } - } - - // Same network award. - if (sameNetwork) { - score += mCurrentNetworkBoost; - sbuf.append(" Same network as the current one, bonus: ").append(mCurrentNetworkBoost); - } - - // Same BSSID award. - if (sameBssid) { - score += mSameBssidAward; - sbuf.append(" Same BSSID as the current one, bonus: ").append(mSameBssidAward); - } - - // Security award. - if (network.isPasspoint()) { - score += mPasspointSecurityAward; - sbuf.append(" Passpoint bonus: ").append(mPasspointSecurityAward); - } else if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) { - score += mSecurityAward; - sbuf.append(" Secure network bonus: ").append(mSecurityAward); - } - - // No internet penalty. - if (network.numNoInternetAccessReports > 0 && !network.validatedInternetAccess) { - score -= mNoIntnetPenalty; - sbuf.append(" No internet penalty: -").append(mNoIntnetPenalty); - } - - sbuf.append(" -- ScanResult: ").append(scanResult).append(" for network: ") - .append(network.networkId).append(" score: ").append(score).append(" --\n"); - - return score; - } - - /** - * Update all the saved networks' selection status - */ - private void updateSavedNetworkSelectionStatus() { - List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks(); - if (savedNetworks.size() == 0) { - localLog("no saved network"); - return; - } - - StringBuffer sbuf = new StringBuffer("Saved Network List: \n"); - for (WifiConfiguration network : savedNetworks) { - WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); - // If a configuration is temporarily disabled, re-enable it before trying - // to connect to it. - mWifiConfigManager.tryEnableNetwork(network.networkId); - - // Clear the cached candidate, score and seen. - mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); - - sbuf.append(" ").append(getNetworkString(network)).append(" ") - .append(" User Preferred BSSID: ").append(network.BSSID) - .append(" FQDN: ").append(network.FQDN).append(" ") - .append(status.getNetworkStatusString()).append(" Disable account: "); - for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE; - index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; - index++) { - sbuf.append(status.getDisableReasonCounter(index)).append(" "); - } - sbuf.append("Connect Choice: ").append(status.getConnectChoice()) - .append(" set time: ").append(status.getConnectChoiceTimestamp()) - .append("\n"); - } - localLog(sbuf.toString()); - } - - /** - * This API is called when user/app explicitly selects a network. Currently, it is used in - * following cases: - * (1) User explicitly chooses to connect to a saved network. - * (2) User saves a network after adding a new network. - * (3) User saves a network after modifying a saved network. - * Following actions will be triggered: - * 1. If this network is disabled, we need re-enable it again. - * 2. This network is favored over all the other networks visible in latest network - * selection procedure. - * - * @param netId ID for the network chosen by the user - * @return true -- There is change made to connection choice of any saved network. - * false -- There is no change made to connection choice of any saved network. - */ - public boolean setUserConnectChoice(int netId) { - localLog("setUserConnectChoice: network ID=" + netId); - WifiConfiguration selected = mWifiConfigManager.getConfiguredNetwork(netId); - if (selected == null || selected.SSID == null) { - localLoge("setUserConnectChoice: Invalid configuration with nid=" + netId); - return false; - } - - boolean change = false; - String key = selected.configKey(); - // This is only used for setting the connect choice timestamp for debugging purposes. - long currentTime = mClock.getWallClockMillis(); - List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks(); - for (WifiConfiguration network : savedNetworks) { - WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); - if (network.networkId == selected.networkId) { - if (status.getConnectChoice() != null) { - localLog("Clear connection choice of " + status.getConnectChoice() - + " Set Time: " + status.getConnectChoiceTimestamp() + " from " - + getNetworkString(network)); - mWifiConfigManager.clearNetworkConnectChoice(network.networkId); - change = true; - } - continue; - } - - if (status.getSeenInLastQualifiedNetworkSelection() - && (status.getConnectChoice() == null - || !status.getConnectChoice().equals(key))) { - localLog("Set connection choice of " + key + " Set Time: " + currentTime + " to " - + getNetworkString(network)); - mWifiConfigManager.setNetworkConnectChoice(network.networkId, key, currentTime); - change = true; - } - } - - return change; - } - - /** - * Enable/disable a BSSID for Quality Network Selection - * When an association rejection event is obtained, Quality Network Selector will disable this - * BSSID but supplicant still can try to connect to this bssid. If supplicant connect to it - * successfully later, this bssid can be re-enabled. - * - * @param bssid the bssid to be enabled / disabled - * @param enable -- true enable a bssid if it has been disabled - * -- false disable a bssid - */ - public boolean enableBssidForQualityNetworkSelection(String bssid, boolean enable) { - if (enable) { - return (mBssidBlacklist.remove(bssid) != null); - } else { - if (bssid != null) { - BssidBlacklistStatus status = mBssidBlacklist.get(bssid); - if (status == null) { - // First time for this BSSID - BssidBlacklistStatus newStatus = new BssidBlacklistStatus(); - newStatus.mCounter++; - mBssidBlacklist.put(bssid, newStatus); - } else if (!status.mIsBlacklisted) { - status.mCounter++; - if (status.mCounter >= BSSID_BLACKLIST_THRESHOLD) { - status.mIsBlacklisted = true; - status.mBlacklistedTimeStamp = mClock.getElapsedSinceBootMillis(); - return true; - } - } - } - } - return false; - } - - /** - * Update the buffered BSSID blacklist - * - * Go through the whole buffered BSSIDs blacklist and check when the BSSIDs is blocked. If they - * have been blacklisted for BSSID_BLACKLIST_EXPIRE_TIME_MS, re-enable them. - */ - private void updateBssidBlacklist() { - Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator(); - while (iter.hasNext()) { - BssidBlacklistStatus status = iter.next(); - if (status != null && status.mIsBlacklisted) { - if (mClock.getElapsedSinceBootMillis() - status.mBlacklistedTimeStamp - >= BSSID_BLACKLIST_EXPIRE_TIME_MS) { - iter.remove(); - } - } - } - } - - /** - * Check whether a bssid is disabled - * @param bssid -- the bssid to check - */ - public boolean isBssidDisabled(String bssid) { - BssidBlacklistStatus status = mBssidBlacklist.get(bssid); - return status == null ? false : status.mIsBlacklisted; - } - - /** - * Select the best network candidate from the new scan results for WifiConnectivityManager - * to connect/roam to. - * - * @param forceSelectNetwork true -- start a qualified network selection anyway, no matter - * the current network is already qualified or not. - * false -- if current network is already qualified, stay connected - * to it. - * @param isUntrustedConnectionsAllowed connection to untrusted networks is allowed or not - * @param isLinkDebouncing Link layer is under debouncing or not - * @param isConnected WifiStateMachine is in the Connected state or not - * @param isDisconnected WifiStateMachine is in the Disconnected state or not - * @param isSupplicantTransient wpa_supplicant is in a transient state or not - * @param scanDetails new connectivity scan results - * @return Best network candidate identified. Null if no candidate available or we should - * stay connected to the current network. - */ - public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork, - boolean isUntrustedConnectionsAllowed, boolean isLinkDebouncing, - boolean isConnected, boolean isDisconnected, boolean isSupplicantTransient, - List<ScanDetail> scanDetails) { - localLog("==========start qualified Network Selection=========="); - - List<Pair<ScanDetail, WifiConfiguration>> filteredScanDetails = new ArrayList<>(); - - if (scanDetails.size() == 0) { - localLog("Empty connectivity scan result"); - mFilteredScanDetails = filteredScanDetails; - return null; - } - - if (mCurrentConnectedNetwork == null) { - mCurrentConnectedNetwork = - mWifiConfigManager.getConfiguredNetwork(mWifiInfo.getNetworkId()); - } - - if (mCurrentBssid == null) { - mCurrentBssid = mWifiInfo.getBSSID(); - } - - if (!forceSelectNetwork && !needQualifiedNetworkSelection(isLinkDebouncing, isConnected, - isDisconnected, isSupplicantTransient)) { - localLog("Stay connected to the current qualified network"); - mFilteredScanDetails = filteredScanDetails; - return null; - } - - int currentHighestScore = Integer.MIN_VALUE; - ScanResult scanResultCandidate = null; - WifiConfiguration networkCandidate = null; - final ExternalScoreEvaluator externalScoreEvaluator = - new ExternalScoreEvaluator(mLocalLog, mDbg); - int lastUserSelectedNetWork = mWifiConfigManager.getLastSelectedNetwork(); - WifiConfiguration lastUserSelectedNetwork = - mWifiConfigManager.getConfiguredNetwork(lastUserSelectedNetWork); - if (lastUserSelectedNetwork != null) { - localLog("Last selection is " + lastUserSelectedNetwork.SSID + " Time to now: " - + ((mClock.getElapsedSinceBootMillis() - - mWifiConfigManager.getLastSelectedTimeStamp()) / 1000 / 60 + " minutes")); - } - - updateSavedNetworkSelectionStatus(); - updateBssidBlacklist(); - - StringBuffer lowSignalScan = new StringBuffer(); - StringBuffer notSavedScan = new StringBuffer(); - StringBuffer noValidSsid = new StringBuffer(); - StringBuffer unwantedBand = new StringBuffer(); - StringBuffer scoreHistory = new StringBuffer(); - ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>(); - - // Iterate over all scan results to find the best candidate. - for (ScanDetail scanDetail : scanDetails) { - ScanResult scanResult = scanDetail.getScanResult(); - // Skip bad scan result. - if (scanResult.SSID == null || TextUtils.isEmpty(scanResult.SSID)) { - if (mDbg) { - noValidSsid.append(scanResult.BSSID).append(" / "); - } - continue; - } - - final String scanId = toScanId(scanResult); - // Skip blacklisted BSSID. - if (isBssidDisabled(scanResult.BSSID)) { - Log.i(TAG, scanId + " is in the blacklist."); - continue; - } - - // Skip network with too weak signals. - if ((scanResult.is24GHz() && scanResult.level < mThresholdMinimumRssi24) - || (scanResult.is5GHz() && scanResult.level < mThresholdMinimumRssi5)) { - if (mDbg) { - lowSignalScan.append(scanId).append("(") - .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz") - .append(")").append(scanResult.level).append(" / "); - } - continue; - } - - // Skip network not matching band preference set by user. - // WifiConnectivityManager schedules scan according to the user band prefrence. This is - // a check for the ScanResults generated from the old settings. - if ((scanResult.is24GHz() - && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)) - || (scanResult.is5GHz() - && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ))) { - if (mDbg) { - unwantedBand.append(scanId).append("(") - .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz") - .append(")").append(" / "); - } - continue; - } - - // Is there a score for this network? If not, request a score. - if (mNetworkScoreCache != null && !mNetworkScoreCache.isScoredNetwork(scanResult)) { - WifiKey wifiKey; - try { - wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID); - NetworkKey ntwkKey = new NetworkKey(wifiKey); - // Add to the unscoredNetworks list so we can request score later - unscoredNetworks.add(ntwkKey); - } catch (IllegalArgumentException e) { - Log.w(TAG, "Invalid SSID=" + scanResult.SSID + " BSSID=" + scanResult.BSSID - + " for network score. Skip."); - } - } - - // Is this scan result from an ephemeral network? - boolean potentiallyEphemeral = false; - // Stores WifiConfiguration of potential connection candidates for scan result filtering - WifiConfiguration potentialEphemeralCandidate = null; - // TODO(b/31065385): WifiConfigManager does not support passpoint networks currently. - // So this list has just one entry always. - List<WifiConfiguration> associatedWifiConfigurations = null; - WifiConfiguration associatedWifiConfiguration = - mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail); - if (associatedWifiConfiguration != null) { - associatedWifiConfigurations = - new ArrayList<>(Arrays.asList(associatedWifiConfiguration)); - } - if (associatedWifiConfigurations == null) { - potentiallyEphemeral = true; - if (mDbg) { - notSavedScan.append(scanId).append(" / "); - } - } else if (associatedWifiConfigurations.size() == 1) { - // If there is more than one associated network, it must be a passpoint network. - WifiConfiguration network = associatedWifiConfigurations.get(0); - if (network.ephemeral) { - potentialEphemeralCandidate = network; - potentiallyEphemeral = true; - } - } - - // Evaluate the potentially ephemeral network as a possible candidate if untrusted - // connections are allowed and we have an external score for the scan result. - if (potentiallyEphemeral) { - if (isUntrustedConnectionsAllowed) { - Integer netScore = getNetworkScore(scanResult, false); - if (netScore != null - && !mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) { - externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult); - // scanDetail is for available ephemeral network - filteredScanDetails.add(Pair.create(scanDetail, - potentialEphemeralCandidate)); - } - } - continue; - } - - // Calculate the score of each ScanResult whose associated network is not ephemeral. - // One ScanResult can associated with more than one network, hence we calculate all - // the scores and use the highest one as the ScanResult's score - int highestScore = Integer.MIN_VALUE; - int score; - int candidateNetworkIdForThisScan = WifiConfiguration.INVALID_NETWORK_ID; - WifiConfiguration potentialCandidate = null; - for (WifiConfiguration network : associatedWifiConfigurations) { - WifiConfiguration.NetworkSelectionStatus status = - network.getNetworkSelectionStatus(); - if (potentialCandidate == null) { - potentialCandidate = network; - } - 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 " + getNetworkString(network) + " has specified BSSID " - + network.BSSID + ". Skip " + scanResult.BSSID); - continue; - } - - // If the network is marked to use external scores then attempt to fetch the score. - // These networks will not be considered alongside the other saved networks. - if (network.useExternalScores) { - Integer netScore = getNetworkScore(scanResult, false); - externalScoreEvaluator.evalSavedCandidate(netScore, network, scanResult); - continue; - } - boolean sameNetwork = - mCurrentConnectedNetwork == null - ? false - : (mCurrentConnectedNetwork.networkId == network.networkId - || network.isLinked(mCurrentConnectedNetwork)); - boolean sameBssid = - mCurrentBssid == null - ? false - : mCurrentBssid.equals(scanResult.BSSID); - boolean sameSelect = - lastUserSelectedNetwork == null - ? false - : lastUserSelectedNetwork.networkId == network.networkId; - score = calculateBssidScore(scanResult, network, sameNetwork, sameBssid, sameSelect, - scoreHistory); - if (score > highestScore) { - highestScore = score; - candidateNetworkIdForThisScan = network.networkId; - potentialCandidate = network; - } - - // Update the cached candidate. - if (score > status.getCandidateScore() || (score == status.getCandidateScore() - && status.getCandidate() != null - && scanResult.level > status.getCandidate().level)) { - mWifiConfigManager.setNetworkCandidateScanResult( - candidateNetworkIdForThisScan, scanResult, score); - } - } - // Create potential filteredScanDetail entry. - filteredScanDetails.add(Pair.create(scanDetail, potentialCandidate)); - - if (highestScore > currentHighestScore || (highestScore == currentHighestScore - && scanResultCandidate != null - && scanResult.level > scanResultCandidate.level)) { - currentHighestScore = highestScore; - scanResultCandidate = scanResult; - mWifiConfigManager.setNetworkCandidateScanResult( - candidateNetworkIdForThisScan, scanResultCandidate, currentHighestScore); - // Reload the network config with the updated info. - networkCandidate = - mWifiConfigManager.getConfiguredNetwork(candidateNetworkIdForThisScan); - } - } - - mFilteredScanDetails = filteredScanDetails; - - // Kick the score manager if there is any unscored network. - if (mScoreManager != null && unscoredNetworks.size() != 0) { - NetworkKey[] unscoredNetworkKeys = - unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]); - mScoreManager.requestScores(unscoredNetworkKeys); - } - - if (mDbg) { - if (lowSignalScan.length() != 0) { - localLog(lowSignalScan + " skipped due to low signal"); - } - if (notSavedScan.length() != 0) { - localLog(notSavedScan + " skipped due to not saved"); - } - if (noValidSsid.length() != 0) { - localLog(noValidSsid + " skipped due to invalid SSID"); - } - if (unwantedBand.length() != 0) { - localLog(unwantedBand + " skipped due to user band preference"); - } - localLog(scoreHistory.toString()); - } - - // Traverse the whole user preference to choose the one user likes the most. - if (scanResultCandidate != null) { - WifiConfiguration tempConfig = networkCandidate; - while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) { - String key = tempConfig.getNetworkSelectionStatus().getConnectChoice(); - tempConfig = mWifiConfigManager.getConfiguredNetwork(key); - if (tempConfig != null) { - WifiConfiguration.NetworkSelectionStatus tempStatus = - tempConfig.getNetworkSelectionStatus(); - if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) { - scanResultCandidate = tempStatus.getCandidate(); - networkCandidate = tempConfig; - } - } else { - // We should not come here in theory. - localLoge("Connect choice: " + key + " has no corresponding saved config"); - break; - } - } - localLog("After user choice adjustment, the final candidate is:" - + getNetworkString(networkCandidate) + " : " + scanResultCandidate.BSSID); - } - - // At this point none of the saved networks were good candidates so we fall back to - // externally scored networks if any are available. - if (scanResultCandidate == null) { - localLog("Checking the externalScoreEvaluator for candidates..."); - networkCandidate = - getExternalScoreCandidateNetwork(externalScoreEvaluator); - if (networkCandidate != null) { - scanResultCandidate = networkCandidate.getNetworkSelectionStatus().getCandidate(); - } - } - - if (scanResultCandidate == null) { - localLog("Can not find any suitable candidates"); - return null; - } - - String currentAssociationId = mCurrentConnectedNetwork == null ? "Disconnected" : - getNetworkString(mCurrentConnectedNetwork); - String targetAssociationId = getNetworkString(networkCandidate); - // TODO(b/31065385): In passpoint, saved configuration is initialized with a fake SSID. - // Now update it with the real SSID from the scan result. - - mCurrentBssid = scanResultCandidate.BSSID; - mCurrentConnectedNetwork = networkCandidate; - mLastQualifiedNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); - return networkCandidate; - } - - /** - * Returns the best candidate network according to the given ExternalScoreEvaluator. - */ - @Nullable - WifiConfiguration getExternalScoreCandidateNetwork(ExternalScoreEvaluator scoreEvaluator) { - int candidateNetworkId = WifiConfiguration.INVALID_NETWORK_ID; - switch (scoreEvaluator.getBestCandidateType()) { - case ExternalScoreEvaluator.BestCandidateType.UNTRUSTED_NETWORK: - ScanResult untrustedScanResultCandidate = - scoreEvaluator.getScanResultCandidate(); - WifiConfiguration unTrustedNetworkCandidate = - ScanResultUtil.createNetworkFromScanResult(untrustedScanResultCandidate); - // Mark this config as ephemeral so it isn't persisted. - unTrustedNetworkCandidate.ephemeral = true; - if (mNetworkScoreCache != null) { - unTrustedNetworkCandidate.meteredHint = - mNetworkScoreCache.getMeteredHint(untrustedScanResultCandidate); - } - NetworkUpdateResult result = - mWifiConfigManager.addOrUpdateNetwork( - unTrustedNetworkCandidate, Process.WIFI_UID); - if (!result.isSuccess()) { - Log.e(TAG, "Failed to add ephemeral network"); - break; - } - candidateNetworkId = result.getNetworkId(); - mWifiConfigManager.setNetworkCandidateScanResult( - candidateNetworkId, untrustedScanResultCandidate, 0); - localLog(String.format("new ephemeral candidate %s network ID:%d, " - + "meteredHint=%b", - toScanId(untrustedScanResultCandidate), candidateNetworkId, - unTrustedNetworkCandidate.meteredHint)); - break; - - case ExternalScoreEvaluator.BestCandidateType.SAVED_NETWORK: - ScanResult scanResultCandidate = scoreEvaluator.getScanResultCandidate(); - candidateNetworkId = scoreEvaluator.getSavedConfig().networkId; - mWifiConfigManager.setNetworkCandidateScanResult( - candidateNetworkId, scanResultCandidate, 0); - localLog(String.format("new scored candidate %s network ID:%d", - toScanId(scanResultCandidate), candidateNetworkId)); - break; - - case ExternalScoreEvaluator.BestCandidateType.NONE: - localLog("ExternalScoreEvaluator did not see any good candidates."); - break; - - default: - localLoge("Unhandled ExternalScoreEvaluator case. No candidate selected."); - break; - } - return mWifiConfigManager.getConfiguredNetwork(candidateNetworkId); - } - - /** - * Returns the available external network score or NULL if no score is available. - * - * @param scanResult The scan result of the network to score. - * @param isActiveNetwork Whether or not the network is currently connected. - * @return A valid external score if one is available or NULL. - */ - @Nullable - Integer getNetworkScore(ScanResult scanResult, boolean isActiveNetwork) { - if (mNetworkScoreCache != null && mNetworkScoreCache.isScoredNetwork(scanResult)) { - int networkScore = mNetworkScoreCache.getNetworkScore(scanResult, isActiveNetwork); - localLog(toScanId(scanResult) + " has score: " + networkScore); - return networkScore; - } - return null; - } - - /** - * Formats the given ScanResult as a scan ID for logging. - */ - private static String toScanId(@Nullable ScanResult scanResult) { - return scanResult == null ? "NULL" - : String.format("%s:%s", scanResult.SSID, scanResult.BSSID); - } - - // Dump the logs. - void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Dump of WifiQualifiedNetworkSelector"); - pw.println("WifiQualifiedNetworkSelector - Log Begin ----"); - mLocalLog.dump(fd, pw, args); - pw.println("WifiQualifiedNetworkSelector - Log End ----"); - } - - /** - * Used to track and evaluate networks that are assigned external scores. - */ - static class ExternalScoreEvaluator { - @Retention(RetentionPolicy.SOURCE) - @interface BestCandidateType { - int NONE = 0; - int SAVED_NETWORK = 1; - int UNTRUSTED_NETWORK = 2; - } - // Always set to the best known candidate. - private @BestCandidateType int mBestCandidateType = BestCandidateType.NONE; - private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE; - private WifiConfiguration mSavedConfig; - private ScanResult mScanResultCandidate; - private final LocalLog mLocalLog; - private final boolean mDbg; - - ExternalScoreEvaluator(LocalLog localLog, boolean dbg) { - mLocalLog = localLog; - mDbg = dbg; - } - - // Determines whether or not the given scan result is the best one its seen so far. - void evalUntrustedCandidate(@Nullable Integer score, ScanResult scanResult) { - if (score != null && score > mHighScore) { - mHighScore = score; - mScanResultCandidate = scanResult; - mBestCandidateType = BestCandidateType.UNTRUSTED_NETWORK; - localLog(toScanId(scanResult) + " become the new untrusted candidate"); - } - } - - // Determines whether or not the given saved network is the best one its seen so far. - void evalSavedCandidate(@Nullable Integer score, WifiConfiguration config, - ScanResult scanResult) { - // Always take the highest score. If there's a tie and an untrusted network is currently - // the best then pick the saved network. - if (score != null - && (score > mHighScore - || (mBestCandidateType == BestCandidateType.UNTRUSTED_NETWORK - && score == mHighScore))) { - mHighScore = score; - mSavedConfig = config; - mScanResultCandidate = scanResult; - mBestCandidateType = BestCandidateType.SAVED_NETWORK; - localLog(toScanId(scanResult) + " become the new externally scored saved network " - + "candidate"); - } - } - - int getBestCandidateType() { - return mBestCandidateType; - } - - int getHighScore() { - return mHighScore; - } - - public ScanResult getScanResultCandidate() { - return mScanResultCandidate; - } - - WifiConfiguration getSavedConfig() { - return mSavedConfig; - } - - private void localLog(String log) { - if (mDbg) { - mLocalLog.log(log); - } - } - } -} diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index b96585ed5..69f8836fa 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -193,7 +193,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private WifiConfigManager mWifiConfigManager; private WifiSupplicantControl mWifiSupplicantControl; private WifiConnectivityManager mWifiConnectivityManager; - private WifiQualifiedNetworkSelector mWifiQualifiedNetworkSelector; + private WifiNetworkSelector mWifiNetworkSelector; private INetworkManagementService mNwService; private IWificond mWificond; private IClientInterface mClientInterface; @@ -879,8 +879,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiDiagnostics = mWifiInjector.makeWifiDiagnostics(mWifiNative); mWifiInfo = new WifiInfo(); - mWifiQualifiedNetworkSelector = new WifiQualifiedNetworkSelector(mWifiConfigManager, - mContext, mWifiInfo, mWifiInjector.getClock()); + mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, + mWifiInfo, mWifiInjector.getClock()); mSupplicantStateTracker = mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler()); @@ -1178,10 +1178,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiNative.enableVerboseLogging(verbose); mWifiConfigManager.enableVerboseLogging(verbose); mSupplicantStateTracker.enableVerboseLogging(verbose); - mWifiQualifiedNetworkSelector.enableVerboseLogging(verbose); - if (mWifiConnectivityManager != null) { - mWifiConnectivityManager.enableVerboseLogging(verbose); - } } private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL"; @@ -2137,11 +2133,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss pw.println(); mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_USER_ACTION); mWifiDiagnostics.dump(fd, pw, args); - mWifiQualifiedNetworkSelector.dump(fd, pw, args); dumpIpManager(fd, pw, args); - if (mWifiConnectivityManager != null) { - mWifiConnectivityManager.dump(fd, pw, args); - } + mWifiNetworkSelector.dump(fd, pw, args); } public void handleUserSwitch(int userId) { @@ -4169,7 +4162,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss synchronized (mWifiReqCountLock) { mWifiConnectivityManager = new WifiConnectivityManager(mContext, WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo, - mWifiQualifiedNetworkSelector, mWifiInjector, + mWifiNetworkSelector, mWifiInjector, getHandler().getLooper(), hasConnectionRequests()); mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0); mWifiConnectivityManager.handleScreenStateChanged(mScreenOn); @@ -6177,7 +6170,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */) && ((ScanResult.is24GHz(mWifiInfo.getFrequency()) && mWifiInfo.getRssi() > - WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND) + mThresholdQualifiedRssi5) || (ScanResult.is5GHz(mWifiInfo.getFrequency()) && mWifiInfo.getRssi() > mThresholdQualifiedRssi5))) { diff --git a/tests/wifitests/src/com/android/server/wifi/ExternalScoreEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/ExternalScoreEvaluatorTest.java new file mode 100644 index 000000000..16bd4f8d8 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/ExternalScoreEvaluatorTest.java @@ -0,0 +1,419 @@ +/* + * 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 static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; +import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.app.test.MockAnswerUtil.AnswerWithArguments; +import android.content.Context; +import android.content.res.Resources; +import android.net.INetworkScoreCache; +import android.net.NetworkScoreManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.os.SystemClock; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.internal.R; +import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.ExternalScoreEvaluator}. + */ +@SmallTest +public class ExternalScoreEvaluatorTest { + + /** Sets up test. */ + @Before + public void setUp() throws Exception { + mResource = getResource(); + mScoreManager = getScoreManager(); + mContext = getContext(); + mWifiConfigManager = getWifiConfigManager(); + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); + + mThresholdQualifiedRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); + mThresholdQualifiedRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); + + mExternalScoreEvaluator = new ExternalScoreEvaluator(mContext, mWifiConfigManager, + mClock, null); + } + + /** Cleans up test. */ + @After + public void cleanup() { + validateMockitoUsage(); + } + + private ExternalScoreEvaluator mExternalScoreEvaluator; + private WifiConfigManager mWifiConfigManager; + private Context mContext; + private Resources mResource; + private NetworkScoreManager mScoreManager; + private WifiNetworkScoreCache mScoreCache; + private Clock mClock = mock(Clock.class); + private int mThresholdQualifiedRssi2G; + private int mThresholdQualifiedRssi5G; + private static final String TAG = "External Score Evaluator Unit Test"; + + NetworkScoreManager getScoreManager() { + NetworkScoreManager scoreManager = mock(NetworkScoreManager.class); + + doAnswer(new AnswerWithArguments() { + public void answer(int networkType, INetworkScoreCache scoreCache) { + mScoreCache = (WifiNetworkScoreCache) scoreCache; + }}).when(scoreManager).registerNetworkScoreCache(anyInt(), anyObject()); + + return scoreManager; + } + + Context getContext() { + Context context = mock(Context.class); + + when(context.getResources()).thenReturn(mResource); + when(context.getSystemService(Context.NETWORK_SCORE_SERVICE)).thenReturn(mScoreManager); + + return context; + } + + Resources getResource() { + Resources resource = mock(Resources.class); + + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) + .thenReturn(-70); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)) + .thenReturn(-73); + + return resource; + } + + WifiConfigManager getWifiConfigManager() { + WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); + when(wifiConfigManager.getLastSelectedNetwork()) + .thenReturn(WifiConfiguration.INVALID_NETWORK_ID); + return wifiConfigManager; + } + + + /** + * When no saved networks available, choose the available ephemeral networks + * if untrusted networks are allowed. + */ + @Test + public void chooseEphemeralNetworkBecauseOfNoSavedNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10}; + Integer[] scores = {null, 120}; + boolean[] meteredHints = {false, true}; + + List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( + ssids, bssids, freqs, caps, levels, mClock); + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + // No saved networks. + when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class))) + .thenReturn(null); + + ScanResult scanResult = scanDetails.get(1).getScanResult(); + WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil + .setupEphemeralNetwork(mWifiConfigManager, 1, scanResult, meteredHints[1]); + + // Untrusted networks allowed. + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + scanResult, candidate); + assertEquals(meteredHints[1], candidate.meteredHint); + } + + /** + * When no saved networks available, choose the highest scored ephemeral networks + * if untrusted networks are allowed. + */ + @Test + public void chooseHigherScoredEphemeralNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[ESS]", "[ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8}; + Integer[] scores = {100, 120}; + boolean[] meteredHints = {true, true}; + ScanResult[] scanResults = new ScanResult[2]; + WifiConfiguration[] ephemeralNetworkConfigs = new WifiConfiguration[2]; + + List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( + ssids, bssids, freqs, caps, levels, mClock); + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + // No saved networks. + when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class))) + .thenReturn(null); + + for (int i = 0; i < 2; i++) { + scanResults[i] = scanDetails.get(i).getScanResult(); + ephemeralNetworkConfigs[i] = WifiNetworkSelectorTestUtil + .setupEphemeralNetwork(mWifiConfigManager, i, scanResults[i], meteredHints[i]); + } + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + scanResults[1], candidate); + assertEquals(meteredHints[1], candidate.meteredHint); + } + + /** + * Don't choose available ephemeral networks if no saved networks and untrusted networks + * are not allowed. + */ + @Test + public void noEphemeralNetworkWhenUntrustedNetworksNotAllowed() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10}; + Integer[] scores = {null, 120}; + boolean[] meteredHints = {false, true}; + + List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( + ssids, bssids, freqs, caps, levels, mClock); + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + // No saved networks. + when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class))) + .thenReturn(null); + + ScanResult scanResult = scanDetails.get(1).getScanResult(); + WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil + .setupEphemeralNetwork(mWifiConfigManager, 1, scanResult, meteredHints[1]); + + // Untursted networks not allowed. + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, false, null); + + assertEquals("Expect null configuration", null, candidate); + } + + + /** + * Choose externally scored saved network. + */ + @Test + public void chooseSavedNetworkWithExternalScore() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {5200}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] securities = {SECURITY_PSK}; + int[] levels = {mThresholdQualifiedRssi5G + 8}; + Integer[] scores = {120}; + boolean[] meteredHints = {false}; + + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].useExternalScores = true; + + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + scanDetails.get(0).getScanResult(), candidate); + } + + /** + * Choose externally scored saved network with higher score. + */ + @Test + public void chooseSavedNetworkWithHigherExternalScore() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8}; + Integer[] scores = {100, 120}; + boolean[] meteredHints = {false, false}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].useExternalScores = savedConfigs[1].useExternalScores = true; + + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + scanDetails.get(1).getScanResult(), candidate); + } + + /** + * Prefer externally scored saved network over untrusted network when they have + * the same score. + */ + @Test + public void chooseExternallyScoredSavedNetworkOverUntrustedNetworksWithSameScore() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; + int[] securities = {SECURITY_PSK, SECURITY_NONE}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8}; + Integer[] scores = {120, 120}; + boolean[] meteredHints = {false, true}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].useExternalScores = true; + + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + scanDetails.get(0).getScanResult(), candidate); + } + + /** + * Choose untrusted network when it has higher score than the externally scored + * saved network. + */ + @Test + public void chooseUntrustedNetworkWithHigherScoreThanExternallyScoredSavedNetwork() { + // Saved network. + String[] savedSsids = {"\"test1\""}; + String[] savedBssids = {"6c:f3:7f:ae:8c:f3"}; + int[] savedFreqs = {2470}; + String[] savedCaps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] savedSecurities = {SECURITY_PSK}; + int[] savedLevels = {mThresholdQualifiedRssi2G + 8}; + // Ephemeral network. + String[] ephemeralSsids = {"\"test2\""}; + String[] ephemeralBssids = {"6c:f3:7f:ae:8c:f4"}; + int[] ephemeralFreqs = {2437}; + String[] ephemeralCaps = {"[ESS]"}; + int[] ephemeralLevels = {mThresholdQualifiedRssi2G + 8}; + // Ephemeral network has higher score than the saved network. + Integer[] scores = {100, 120}; + boolean[] meteredHints = {false, true}; + + // Set up the saved network. + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(savedSsids, + savedBssids, savedFreqs, savedCaps, savedLevels, savedSecurities, + mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].useExternalScores = true; + + // Set up the ephemeral network. + scanDetails.addAll(WifiNetworkSelectorTestUtil.buildScanDetails( + ephemeralSsids, ephemeralBssids, ephemeralFreqs, + ephemeralCaps, ephemeralLevels, mClock)); + ScanResult ephemeralScanResult = scanDetails.get(1).getScanResult(); + WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil + .setupEphemeralNetwork(mWifiConfigManager, 1, ephemeralScanResult, + meteredHints[1]); + + // Set up score cache for both the saved network and the ephemeral network. + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + ephemeralScanResult, candidate); + } + + /** + * Prefer externally scored saved network over untrusted network when they have + * the same score. + */ + @Test + public void nullScoredNetworks() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; + int[] securities = {SECURITY_PSK, SECURITY_NONE}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8}; + Integer[] scores = {null, null}; + boolean[] meteredHints = {false, true}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].useExternalScores = true; + + WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache, + scanDetails, scores, meteredHints); + + WifiConfiguration candidate = mExternalScoreEvaluator.evaluateNetworks(scanDetails, + null, null, false, true, null); + + assertEquals("Expect null configuration", null, candidate); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java new file mode 100644 index 000000000..fe290cd8a --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java @@ -0,0 +1,323 @@ +/* + * 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 static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; +import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.content.Context; +import android.content.res.Resources; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.os.SystemClock; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.internal.R; +import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.SavedNetworkEvaluator}. + */ +@SmallTest +public class SavedNetworkEvaluatorTest { + + /** Sets up test. */ + @Before + public void setUp() throws Exception { + mResource = getResource(); + mContext = getContext(); + mWifiConfigManager = getWifiConfigManager(); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); + + mThresholdMinimumRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + mThresholdMinimumRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + mThresholdQualifiedRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); + mThresholdQualifiedRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); + mThresholdSaturatedRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); + mThresholdSaturatedRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz); + + mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext, mWifiConfigManager, + mClock, null); + } + + /** Cleans up test. */ + @After + public void cleanup() { + validateMockitoUsage(); + } + + private SavedNetworkEvaluator mSavedNetworkEvaluator; + private WifiConfigManager mWifiConfigManager; + private Context mContext; + private Resources mResource; + private Clock mClock = mock(Clock.class); + private int mThresholdMinimumRssi2G; + private int mThresholdMinimumRssi5G; + private int mThresholdQualifiedRssi2G; + private int mThresholdQualifiedRssi5G; + private int mThresholdSaturatedRssi2G; + private int mThresholdSaturatedRssi5G; + private static final String TAG = "Saved Network Evaluator Unit Test"; + + Context getContext() { + Context context = mock(Context.class); + Resources resource = mock(Resources.class); + + when(context.getResources()).thenReturn(mResource); + return context; + } + + Resources getResource() { + Resources resource = mock(Resources.class); + + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz)) + .thenReturn(-70); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) + .thenReturn(-73); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) + .thenReturn(-70); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)) + .thenReturn(-73); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)) + .thenReturn(-82); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)) + .thenReturn(-85); + when(resource.getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_SLOPE)) + .thenReturn(4); + when(resource.getInteger( + R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)) + .thenReturn(85); + when(resource.getInteger( + R.integer.config_wifi_framework_SAME_BSSID_AWARD)) + .thenReturn(24); + when(resource.getInteger( + R.integer.config_wifi_framework_SECURITY_AWARD)) + .thenReturn(80); + when(resource.getInteger( + R.integer.config_wifi_framework_5GHz_preference_boost_factor)) + .thenReturn(16); + when(resource.getInteger( + R.integer.config_wifi_framework_current_network_boost)) + .thenReturn(16); + + return resource; + } + + WifiConfigManager getWifiConfigManager() { + WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); + when(wifiConfigManager.getLastSelectedNetwork()) + .thenReturn(WifiConfiguration.INVALID_NETWORK_ID); + return wifiConfigManager; + } + + + /** + * Between two 2G networks, choose the one with stronger RSSI value if other conditions + * are the same and the RSSI values are not satuarted. + */ + @Test + public void chooseStrongerRssi2GNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2470, 2437}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, null); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Between two 5G networks, choose the one with stronger RSSI value if other conditions + * are the same and the RSSI values are not satuarted. + */ + @Test + public void chooseStrongerRssi5GNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {5200, 5240}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G + 8, mThresholdQualifiedRssi5G + 10}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, null); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Choose secure network over open network if other conditions are the same. + */ + @Test + public void chooseSecureNetworkOverOpenNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {5200, 5240}; + String[] caps = {"[ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G, mThresholdQualifiedRssi5G}; + int[] securities = {SECURITY_NONE, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, null); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Choose 5G network over 2G network if other conditions are the same. + */ + @Test + public void choose5GNetworkOver2GNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2437, 5240}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G, mThresholdQualifiedRssi5G}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, null, true, false, null); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Verify that we stick to the currently connected network if the other one is + * just slightly better scored. + */ + @Test + public void stickToCurrentNetwork() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {5200, 5240}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + // test2 has slightly stronger RSSI value than test1 + int[] levels = {mThresholdMinimumRssi5G + 2, mThresholdMinimumRssi5G + 4}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + // Simuluate we are connected to SSID test1 already. + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + savedConfigs[0], null, true, false, null); + + // Even though test2 has higher RSSI value, test1 is chosen because of the + // currently connected network bonus. + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * Verify that we stick to the currently connected BSSID if the other one is + * just slightly better scored. + */ + @Test + public void stickToCurrentBSSID() { + // Same SSID + String[] ssids = {"\"test1\"", "\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {5200, 5240}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + // test2 has slightly stronger RSSI value than test1 + int[] levels = {mThresholdMinimumRssi5G + 2, mThresholdMinimumRssi5G + 6}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + // Simuluate we are connected to BSSID "6c:f3:7f:ae:8c:f3" already + WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, + null, bssids[0], true, false, null); + + // Even though test2 has higher RSSI value, test1 is chosen because of the + // currently connected BSSID bonus. + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java index 84133acb7..c952f65ff 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java @@ -72,7 +72,7 @@ public class WifiConnectivityManagerTest { mWifiConfigManager = mockWifiConfigManager(); mWifiInfo = getWifiInfo(); mWifiScanner = mockWifiScanner(); - mWifiQNS = mockWifiQualifiedNetworkSelector(); + mWifiNS = mockWifiNetworkSelector(); mWifiConnectivityManager = createConnectivityManager(); mWifiConnectivityManager.setWifiEnabled(true); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); @@ -91,7 +91,7 @@ public class WifiConnectivityManagerTest { private TestAlarmManager mAlarmManager; private TestLooper mLooper = new TestLooper(); private WifiConnectivityManager mWifiConnectivityManager; - private WifiQualifiedNetworkSelector mWifiQNS; + private WifiNetworkSelector mWifiNS; private WifiStateMachine mWifiStateMachine; private WifiScanner mWifiScanner; private WifiConfigManager mWifiConfigManager; @@ -117,10 +117,10 @@ public class WifiConnectivityManagerTest { R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); when(resource.getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) - .thenReturn(WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); + .thenReturn(-60); when(resource.getInteger( R.integer.config_wifi_framework_current_network_boost)) - .thenReturn(WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD); + .thenReturn(16); return resource; } @@ -197,8 +197,8 @@ public class WifiConnectivityManagerTest { return stateMachine; } - WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() { - WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class); + WifiNetworkSelector mockWifiNetworkSelector() { + WifiNetworkSelector ns = mock(WifiNetworkSelector.class); WifiConfiguration candidate = generateWifiConfig( 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null); @@ -208,9 +208,9 @@ public class WifiConnectivityManagerTest { candidateScanResult.BSSID = CANDIDATE_BSSID; candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); - when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(), - anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(candidate); - return qns; + when(ns.selectNetwork(anyObject(), anyBoolean(), anyBoolean(), + anyBoolean())).thenReturn(candidate); + return ns; } WifiInfo getWifiInfo() { @@ -250,7 +250,7 @@ public class WifiConnectivityManagerTest { WifiConnectivityManager createConnectivityManager() { return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner, - mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector, mLooper.getLooper(), true); + mWifiConfigManager, mWifiInfo, mWifiNS, mWifiInjector, mLooper.getLooper(), true); } /** @@ -490,9 +490,9 @@ public class WifiConnectivityManagerTest { * because of their low RSSI values. */ @Test - public void PnoRetryForLowRssiNetwork() { - when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(), - anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(null); + public void pnoRetryForLowRssiNetwork() { + when(mWifiNS.selectNetwork(anyObject(), anyBoolean(), anyBoolean(), + anyBoolean())).thenReturn(null); // Set screen to off mWifiConnectivityManager.handleScreenStateChanged(false); @@ -511,7 +511,7 @@ public class WifiConnectivityManagerTest { .getLowRssiNetworkRetryDelay(); assertEquals(lowRssiNetworkRetryDelayStartValue * 2, - lowRssiNetworkRetryDelayAfterPnoValue); + lowRssiNetworkRetryDelayAfterPnoValue); } /** @@ -546,8 +546,8 @@ public class WifiConnectivityManagerTest { @Test public void watchdogBitePnoGoodIncrementsMetrics() { // Qns returns no candidate after watchdog single scan. - when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(), - anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(null); + when(mWifiNS.selectNetwork(anyObject(), anyBoolean(), anyBoolean(), + anyBoolean())).thenReturn(null); // Set screen to off mWifiConnectivityManager.handleScreenStateChanged(false); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java new file mode 100644 index 000000000..a03e17457 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java @@ -0,0 +1,491 @@ +/* + * 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 static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; +import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.content.Context; +import android.content.res.Resources; +import android.net.NetworkScoreManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.os.SystemClock; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Pair; + +import com.android.internal.R; +import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.WifiNetworkSelector}. + */ +@SmallTest +public class WifiNetworkSelectorTest { + + /** Sets up test. */ + @Before + public void setUp() throws Exception { + mResource = getResource(); + mContext = getContext(); + mWifiConfigManager = getWifiConfigManager(); + mWifiInfo = getWifiInfo(); + + mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, + mWifiInfo, mClock); + mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator, 1); + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); + + mThresholdMinimumRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + mThresholdMinimumRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + mThresholdQualifiedRssi2G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); + mThresholdQualifiedRssi5G = mResource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); + } + + /** Cleans up test. */ + @After + public void cleanup() { + validateMockitoUsage(); + } + + /** + * All this dummy network evaluator does is to pick the very first network + * in the scan results. + */ + public class DummyNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { + private static final String NAME = "DummyNetworkEvaluator"; + private WifiConfigManager mConfigManager; + + /** + * Get the evaluator name. + */ + public String getName() { + return NAME; + } + + /** + * Update thee evaluator. + */ + public void update(List<ScanDetail> scanDetails) { + } + + /** + * Always return the first network in the scan results for connection. + */ + public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, + WifiConfiguration currentNetwork, String currentBssid, boolean connected, + boolean untrustedNetworkAllowed, + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) { + ScanDetail scanDetail = scanDetails.get(0); + mWifiConfigManager.setNetworkCandidateScanResult(0, scanDetail.getScanResult(), 100); + + return mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail); + } + } + + private WifiNetworkSelector mWifiNetworkSelector = null; + private DummyNetworkEvaluator mDummyEvaluator = new DummyNetworkEvaluator(); + private WifiConfigManager mWifiConfigManager = null; + private Context mContext; + private Resources mResource; + private WifiInfo mWifiInfo; + private Clock mClock = mock(Clock.class); + private int mThresholdMinimumRssi2G; + private int mThresholdMinimumRssi5G; + private int mThresholdQualifiedRssi2G; + private int mThresholdQualifiedRssi5G; + + Context getContext() { + Context context = mock(Context.class); + Resources resource = mock(Resources.class); + + when(context.getResources()).thenReturn(mResource); + return context; + } + + Resources getResource() { + Resources resource = mock(Resources.class); + + when(resource.getBoolean( + R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) + .thenReturn(-70); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)) + .thenReturn(-73); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)) + .thenReturn(-82); + when(resource.getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)) + .thenReturn(-85); + return resource; + } + + NetworkScoreManager getNetworkScoreManager() { + NetworkScoreManager networkScoreManager = mock(NetworkScoreManager.class); + + return networkScoreManager; + } + + WifiInfo getWifiInfo() { + WifiInfo wifiInfo = mock(WifiInfo.class); + + // simulate a disconnected state + when(wifiInfo.is24GHz()).thenReturn(true); + when(wifiInfo.is5GHz()).thenReturn(false); + when(wifiInfo.getRssi()).thenReturn(-70); + when(wifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID); + when(wifiInfo.getBSSID()).thenReturn(null); + return wifiInfo; + } + + WifiConfigManager getWifiConfigManager() { + WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); + when(wifiConfigManager.getLastSelectedNetwork()) + .thenReturn(WifiConfiguration.INVALID_NETWORK_ID); + return wifiConfigManager; + } + + + /** + * No network selection if scan result is empty. + * + * WifiStateMachine is in disconnected state. + * scanDetails is empty. + * + * Expected behavior: no network recommended by Network Selector + */ + @Test + public void emptyScanResults() { + String[] ssids = new String[0]; + String[] bssids = new String[0]; + int[] freqs = new int[0]; + String[] caps = new String[0]; + int[] levels = new int[0]; + int[] securities = new int[0]; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + false, true, false); + assertEquals("Expect null configuration", null, candidate); + } + + + /** + * No network selection if the RSSI values in scan result are too low. + * + * WifiStateMachine is in disconnected state. + * scanDetails contains a 2.4GHz and a 5GHz network, but both with RSSI lower than + * the threshold + * + * Expected behavior: no network recommended by Network Selector + */ + @Test + public void verifyMinimumRssiThreshold() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2437, 5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdMinimumRssi2G - 1, mThresholdMinimumRssi5G - 1}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + false, true, false); + assertEquals("Expect null configuration", null, candidate); + } + + /** + * No network selection if WiFi is connected and it is too short from last + * network selection. + * + * WifiStateMachine is in connected state. + * scanDetails contains two valid networks. + * Perform a network seletion right after the first one. + * + * Expected behavior: no network recommended by Network Selector + */ + @Test + public void verifyMinimumTimeGapWhenConnected() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2437, 5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + // Make a network selection. + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + false, true, false); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000); + + // Do another network selection with WSM in CONNECTED state. + candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + true, false, false); + + assertEquals("Expect null configuration", null, candidate); + } + + /** + * Perform network selection if WiFi is disconnected even if it is too short from last + * network selection. + * + * WifiStateMachine is in disconnected state. + * scanDetails contains two valid networks. + * Perform a network seletion right after the first one. + * + * Expected behavior: the first network is recommended by Network Selector + */ + @Test + public void verifyNoMinimumTimeGapWhenDisconnected() { + String[] ssids = {"\"test1\"", "\"test2\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] freqs = {2437, 5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; + int[] securities = {SECURITY_PSK, SECURITY_PSK}; + + // Make a network selection. + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + false, true, false); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000); + + // Do another network selection with WSM in DISCONNECTED state. + candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + false, true, false); + + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * No network selection if the currently connected on is already sufficient. + * + * WifiStateMachine is connected to a qualified (5G, secure, good RSSI) network. + * scanDetails contains a valid network. + * Perform a network seletion after the first one. + * + * Expected behavior: no network recommended by Network Selector + */ + @Test + public void noNetworkSelectionWhenCurrentOneIsSufficient() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G + 8}; + int[] securities = {SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + + // connect to test1 + mWifiNetworkSelector.selectNetwork(scanDetails, false, true, false); + when(mWifiInfo.getNetworkId()).thenReturn(0); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + when(mWifiInfo.is24GHz()).thenReturn(false); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); + + levels[0] = mThresholdQualifiedRssi5G + 20; + scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + scanDetails = scanDetailsAndConfigs.getScanDetails(); + + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + true, false, false); + assertEquals("Expect null configuration", null, candidate); + } + + + /** + * New network selection is performed if the currently connected network + * band is 2G. + * + * WifiStateMachine is connected to a 2G network. + * scanDetails contains a valid networks. + * Perform a network seletion after the first one. + * + * Expected behavior: the first network is recommended by Network Selector + */ + @Test + public void band2GNetworkIsNotSufficient() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {2470}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi2G + 8}; + int[] securities = {SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + // connect to test1 + mWifiNetworkSelector.selectNetwork(scanDetails, false, true, false); + when(mWifiInfo.getNetworkId()).thenReturn(0); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + when(mWifiInfo.is24GHz()).thenReturn(true); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); + + // Do another network selection. + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + true, false, false); + + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + + /** + * New network selection is performed if the currently connected network + * is a open one. + * + * WifiStateMachine is connected to a open network. + * scanDetails contains a valid networks. + * Perform a network seletion after the first one. + * + * Expected behavior: the first network is recommended by Network Selector + */ + @Test + public void openNetworkIsNotSufficient() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {5180}; + String[] caps = {"[ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G + 8}; + int[] securities = {SECURITY_NONE}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + // connect to test1 + mWifiNetworkSelector.selectNetwork(scanDetails, false, true, false); + when(mWifiInfo.getNetworkId()).thenReturn(0); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + when(mWifiInfo.is24GHz()).thenReturn(false); + when(mWifiInfo.is5GHz()).thenReturn(true); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); + + // Do another network selection. + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + true, false, false); + + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } + + /** + * New network selection is performed if the currently connected network + * has low RSSI value. + * + * WifiStateMachine is connected to a low RSSI 5GHz network. + * scanDetails contains a valid networks. + * Perform a network seletion after the first one. + * + * Expected behavior: the first network is recommended by Network Selector + */ + @Test + public void lowRssi5GNetworkIsNotSufficient() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {5180}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G - 2}; + int[] securities = {SECURITY_PSK}; + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + + // connect to test1 + mWifiNetworkSelector.selectNetwork(scanDetails, false, true, false); + when(mWifiInfo.getNetworkId()).thenReturn(0); + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + when(mWifiInfo.is24GHz()).thenReturn(false); + when(mWifiInfo.is5GHz()).thenReturn(true); + when(mWifiInfo.getRssi()).thenReturn(levels[0]); + + when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); + + // Do another network selection. + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, + true, false, false); + + ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); + WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); + WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, + chosenScanResult, candidate); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java new file mode 100644 index 000000000..db3aff14c --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java @@ -0,0 +1,367 @@ +/* + * 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 static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.app.test.MockAnswerUtil.AnswerWithArguments; +import android.net.NetworkKey; +import android.net.RssiCurve; +import android.net.ScoredNetwork; +import android.net.WifiKey; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; +import android.net.wifi.WifiSsid; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.TextUtils; + +import com.android.server.wifi.util.ScanResultUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper for WifiNetworkSelector unit tests. + */ +@SmallTest +public class WifiNetworkSelectorTestUtil { + + /** + * A class that holds a list of scanDetail and their associated WifiConfiguration. + */ + public static class ScanDetailsAndWifiConfigs { + List<ScanDetail> mScanDetails; + WifiConfiguration[] mWifiConfigs; + + ScanDetailsAndWifiConfigs(List<ScanDetail> scanDetails, WifiConfiguration[] configs) { + mScanDetails = scanDetails; + mWifiConfigs = configs; + } + + List<ScanDetail> getScanDetails() { + return mScanDetails; + } + + WifiConfiguration[] getWifiConfigs() { + return mWifiConfigs; + } + } + + /** + * Build a list of ScanDetail based on the caller supplied network SSID, BSSID, + * frequency, capability and RSSI level information. Create the corresponding + * WifiConfiguration for these networks and set up the mocked WifiConfigManager. + * + * @param ssids an array of SSIDs + * @param bssids an array of BSSIDs + * @param freqs an array of the network's frequency + * @param caps an array of the network's capability + * @param levels an array of the network's RSSI levels + * @param securities an array of the network's security setting + * @param wifiConfigManager the mocked WifiConfigManager + * @return the constructed ScanDetail list and WifiConfiguration array + */ + public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids, + String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities, + WifiConfigManager wifiConfigManager, Clock clock) { + List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock); + WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities); + prepareConfigStore(wifiConfigManager, savedConfigs); + scanResultLinkConfiguration(wifiConfigManager, savedConfigs, scanDetails); + + return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs); + } + + /** + * Verify whether the WifiConfiguration chosen by WifiNetworkSelector matches + * with the chosen scan result. + * + * @param chosenScanResult the chosen scan result + * @param chosenCandidate the chosen configuration + */ + public static void verifySelectedScanResult(WifiConfigManager wifiConfigManager, + ScanResult chosenScanResult, WifiConfiguration chosenCandidate) { + verify(wifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult( + eq(chosenCandidate.networkId), eq(chosenScanResult), anyInt()); + } + + + /** + * Build a list of scanDetails based on the caller supplied network SSID, BSSID, + * frequency, capability and RSSI level information. + * + * @param ssids an array of SSIDs + * @param bssids an array of BSSIDs + * @param freqs an array of the network's frequency + * @param caps an array of the network's capability + * @param levels an array of the network's RSSI levels + * @return the constructed list of ScanDetail + */ + public static List<ScanDetail> buildScanDetails(String[] ssids, String[] bssids, int[] freqs, + String[] caps, int[] levels, Clock clock) { + List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>(); + + long timeStamp = clock.getElapsedSinceBootMillis(); + for (int index = 0; index < ssids.length; index++) { + ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssids[index]), + bssids[index], caps[index], levels[index], freqs[index], timeStamp, 0); + scanDetailList.add(scanDetail); + } + return scanDetailList; + } + + + /** + * Generate an array of {@link android.net.wifi.WifiConfiguration} based on the caller + * supplied network SSID and sencurity information. + * + * @param ssids an array of SSIDs + * @param securities an array of the network's security setting + * @return the constructed array of {@link android.net.wifi.WifiConfiguration} + */ + public static WifiConfiguration[] generateWifiConfigurations(String[] ssids, + int[] securities) { + if (ssids == null || securities == null || ssids.length != securities.length + || ssids.length == 0) { + return null; + } + + WifiConfiguration[] configs = new WifiConfiguration[ssids.length]; + for (int index = 0; index < ssids.length; index++) { + configs[index] = generateWifiConfig(index, 0, ssids[index], false, true, null, null, + securities[index]); + } + + return configs; + } + + /** + * Add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according + * to the networkd ID) and setup the WifiConfigManager mocks for these networks. + * This simulates the WifiConfigManager class behaviour. + * + * @param wifiConfigManager the mocked WifiConfigManager + * @param configs input configuration need to be added to WifiConfigureStore + */ + private static void prepareConfigStore(WifiConfigManager wifiConfigManager, + final WifiConfiguration[] configs) { + when(wifiConfigManager.getConfiguredNetwork(anyInt())) + .then(new AnswerWithArguments() { + public WifiConfiguration answer(int netId) { + if (netId >= 0 && netId < configs.length) { + return new WifiConfiguration(configs[netId]); + } else { + return null; + } + } + }); + when(wifiConfigManager.getConfiguredNetwork(anyString())) + .then(new AnswerWithArguments() { + public WifiConfiguration answer(String configKey) { + for (int netId = 0; netId < configs.length; netId++) { + if (TextUtils.equals(configs[netId].configKey(), configKey)) { + return new WifiConfiguration(configs[netId]); + } + } + return null; + } + }); + when(wifiConfigManager.getSavedNetworks()) + .then(new AnswerWithArguments() { + public List<WifiConfiguration> answer() { + List<WifiConfiguration> savedNetworks = new ArrayList<>(); + for (int netId = 0; netId < configs.length; netId++) { + savedNetworks.add(new WifiConfiguration(configs[netId])); + } + return savedNetworks; + } + }); + when(wifiConfigManager.clearNetworkCandidateScanResult(anyInt())) + .then(new AnswerWithArguments() { + public boolean answer(int netId) { + if (netId >= 0 && netId < configs.length) { + configs[netId].getNetworkSelectionStatus().setCandidate(null); + configs[netId].getNetworkSelectionStatus() + .setCandidateScore(Integer.MIN_VALUE); + configs[netId].getNetworkSelectionStatus() + .setSeenInLastQualifiedNetworkSelection(false); + return true; + } else { + return false; + } + } + }); + when(wifiConfigManager.setNetworkCandidateScanResult( + anyInt(), any(ScanResult.class), anyInt())) + .then(new AnswerWithArguments() { + public boolean answer(int netId, ScanResult scanResult, int score) { + if (netId >= 0 && netId < configs.length) { + configs[netId].getNetworkSelectionStatus().setCandidate(scanResult); + configs[netId].getNetworkSelectionStatus().setCandidateScore(score); + configs[netId].getNetworkSelectionStatus() + .setSeenInLastQualifiedNetworkSelection(true); + return true; + } else { + return false; + } + } + }); + when(wifiConfigManager.clearNetworkConnectChoice(anyInt())) + .then(new AnswerWithArguments() { + public boolean answer(int netId) { + if (netId >= 0 && netId < configs.length) { + configs[netId].getNetworkSelectionStatus().setConnectChoice(null); + configs[netId].getNetworkSelectionStatus() + .setConnectChoiceTimestamp( + NetworkSelectionStatus + .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); + return true; + } else { + return false; + } + } + }); + when(wifiConfigManager.setNetworkConnectChoice(anyInt(), anyString(), anyLong())) + .then(new AnswerWithArguments() { + public boolean answer(int netId, String configKey, long timestamp) { + if (netId >= 0 && netId < configs.length) { + configs[netId].getNetworkSelectionStatus().setConnectChoice(configKey); + configs[netId].getNetworkSelectionStatus().setConnectChoiceTimestamp( + timestamp); + return true; + } else { + return false; + } + } + }); + } + + + /** + * Link scan results to the saved configurations. + * + * The shorter of the 2 input params will be used to loop over so the inputs don't + * need to be of equal length. If there are more scan details then configs the remaining scan + * details will be associated with a NULL config. + * + * @param wifiConfigManager the mocked WifiConfigManager + * @param configs saved configurations + * @param scanDetails come in scan results + */ + private static void scanResultLinkConfiguration(WifiConfigManager wifiConfigManager, + WifiConfiguration[] configs, List<ScanDetail> scanDetails) { + if (configs == null || scanDetails == null) { + return; + } + + if (scanDetails.size() <= configs.length) { + for (int i = 0; i < scanDetails.size(); i++) { + ScanDetail scanDetail = scanDetails.get(i); + when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail))) + .thenReturn(configs[i]); + } + } else { + for (int i = 0; i < configs.length; i++) { + ScanDetail scanDetail = scanDetails.get(i); + when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail))) + .thenReturn(configs[i]); + } + + // associated the remaining scan details with a NULL config. + for (int i = configs.length; i < scanDetails.size(); i++) { + when(wifiConfigManager.getSavedNetworkForScanDetailAndCache( + eq(scanDetails.get(i)))).thenReturn(null); + } + } + } + + + + /** + * Configure the score cache for externally scored networks + * + * @param scoreCache Wifi network score cache to be configured + * @param scanDetails a list of ScanDetail + * @param scores scores of the networks + * @param meteredHints hints of if the networks are metered + */ + public static void configureScoreCache(WifiNetworkScoreCache scoreCache, + List<ScanDetail> scanDetails, Integer[] scores, boolean[] meteredHints) { + List<ScoredNetwork> networks = new ArrayList<>(); + + for (int i = 0; i < scanDetails.size(); i++) { + ScanDetail scanDetail = scanDetails.get(i); + byte rssiScore; + Integer score = scores[i]; + ScanResult scanResult = scanDetail.getScanResult(); + WifiKey wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID); + NetworkKey ntwkKey = new NetworkKey(wifiKey); + if (scores[i] == null) { + rssiScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE; + } else { + rssiScore = scores[i].byteValue(); + } + RssiCurve rssiCurve = new RssiCurve(-100, 100, new byte[] {rssiScore}); + ScoredNetwork scoredNetwork = new ScoredNetwork(ntwkKey, rssiCurve, meteredHints[i]); + + networks.add(scoredNetwork); + } + + scoreCache.updateScores(networks); + } + + /** + * Setup WifiConfigManager mock for ephemeral networks. + * + * @param wifiConfigManager WifiConfigManager mock + * @param networkId ID of the ephemeral network + * @param scanResult scanResult of the ephemeral network + * @param meteredHint flag to indidate if the network has meteredHint + */ + public static WifiConfiguration setupEphemeralNetwork(WifiConfigManager wifiConfigManager, + int networkId, ScanResult scanResult, boolean meteredHint) { + // Return the correct networkID for ephemeral network addition. + when(wifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) + .thenReturn(new NetworkUpdateResult(networkId)); + final WifiConfiguration config = ScanResultUtil.createNetworkFromScanResult(scanResult); + config.networkId = networkId; + config.meteredHint = meteredHint; + + when(wifiConfigManager.getConfiguredNetwork(eq(networkId))) + .then(new AnswerWithArguments() { + public WifiConfiguration answer(int netId) { + return new WifiConfiguration(config); + } + }); + when(wifiConfigManager.setNetworkCandidateScanResult( + eq(networkId), any(ScanResult.class), anyInt())) + .then(new AnswerWithArguments() { + public boolean answer(int netId, ScanResult scanResult, int score) { + config.getNetworkSelectionStatus().setCandidate(scanResult); + config.getNetworkSelectionStatus().setCandidateScore(score); + config.getNetworkSelectionStatus() + .setSeenInLastQualifiedNetworkSelection(true); + return true; + } + }); + return config; + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java deleted file mode 100644 index c83400b93..000000000 --- a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java +++ /dev/null @@ -1,2299 +0,0 @@ -/* - * Copyright (C) 2015 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 static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_EAP; -import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; -import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; -import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import android.app.test.MockAnswerUtil.AnswerWithArguments; -import android.content.Context; -import android.content.res.Resources; -import android.net.NetworkScoreManager; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; -import android.net.wifi.WifiEnterpriseConfig; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiSsid; -import android.os.SystemClock; -import android.test.suitebuilder.annotation.SmallTest; -import android.text.TextUtils; -import android.util.LocalLog; - -import com.android.internal.R; -import com.android.server.wifi.util.ScanResultUtil; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -/** - * Unit tests for {@link com.android.server.wifi.WifiQualifiedNetworkSelector}. - */ -@SmallTest -public class WifiQualifiedNetworkSelectorTest { - - @Before - public void setUp() throws Exception { - mResource = getResource(); - mScoreManager = getNetworkScoreManager(); - mScoreCache = getScoreCache(); - mContext = getContext(); - mWifiConfigManager = getWifiConfigManager(); - mWifiInfo = getWifiInfo(); - mLocalLog = getLocalLog(); - - mWifiQualifiedNetworkSelector = new WifiQualifiedNetworkSelector(mWifiConfigManager, - mContext, mWifiInfo, mClock); - mWifiQualifiedNetworkSelector.enableVerboseLogging(1); - mWifiQualifiedNetworkSelector.setWifiNetworkScoreCache(mScoreCache); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); - } - - @After - public void cleanup() { - validateMockitoUsage(); - } - - private WifiQualifiedNetworkSelector mWifiQualifiedNetworkSelector = null; - private WifiConfigManager mWifiConfigManager = null; - private Context mContext; - private Resources mResource; - private NetworkScoreManager mScoreManager; - private WifiNetworkScoreCache mScoreCache; - private WifiInfo mWifiInfo; - private LocalLog mLocalLog; - private Clock mClock = mock(Clock.class); - private static final String[] DEFAULT_SSIDS = {"\"test1\"", "\"test2\""}; - private static final String[] DEFAULT_BSSIDS = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - private static final String TAG = "QNS Unit Test"; - - private List<ScanDetail> getScanDetails(String[] ssids, String[] bssids, int[] frequencies, - String[] caps, int[] levels) { - List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>(); - long timeStamp = mClock.getElapsedSinceBootMillis(); - for (int index = 0; index < ssids.length; index++) { - ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssids[index]), - bssids[index], caps[index], levels[index], frequencies[index], timeStamp, 0); - scanDetailList.add(scanDetail); - } - return scanDetailList; - } - - Context getContext() { - Context context = mock(Context.class); - Resources resource = mock(Resources.class); - - when(context.getResources()).thenReturn(mResource); - when(context.getSystemService(Context.NETWORK_SCORE_SERVICE)).thenReturn(mScoreManager); - return context; - } - - Resources getResource() { - Resources resource = mock(Resources.class); - - when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80); - when(resource.getInteger(R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)).thenReturn(85); - when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24); - when(resource.getInteger(R.integer.config_wifi_framework_LAST_SELECTION_AWARD)) - .thenReturn(480); - when(resource.getInteger(R.integer.config_wifi_framework_PASSPOINT_SECURITY_AWARD)) - .thenReturn(40); - when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80); - when(resource.getInteger(R.integer.config_wifi_framework_RSSI_SCORE_SLOPE)).thenReturn(4); - when(resource.getBoolean( - R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); - when(resource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) - .thenReturn(WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); - when(resource.getInteger( - R.integer.config_wifi_framework_current_network_boost)) - .thenReturn(WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD); - when(resource.getInteger( - R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) - .thenReturn(WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); - when(resource.getInteger( - R.integer.config_wifi_framework_5GHz_preference_boost_factor)) - .thenReturn(WifiQualifiedNetworkSelector.BAND_AWARD_5GHz); - when(resource.getInteger( - R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) - .thenReturn(WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND); - when(resource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)) - .thenReturn(WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI); - when(resource.getInteger( - R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)) - .thenReturn(WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI); - return resource; - } - - NetworkScoreManager getNetworkScoreManager() { - NetworkScoreManager networkScoreManager = mock(NetworkScoreManager.class); - - return networkScoreManager; - } - - WifiNetworkScoreCache getScoreCache() { - return mock(WifiNetworkScoreCache.class); - } - - LocalLog getLocalLog() { - return new LocalLog(0); - } - - WifiInfo getWifiInfo() { - WifiInfo wifiInfo = mock(WifiInfo.class); - - //simulate a disconnected state - when(wifiInfo.is24GHz()).thenReturn(true); - when(wifiInfo.is5GHz()).thenReturn(false); - when(wifiInfo.getRssi()).thenReturn(-70); - when(wifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID); - when(wifiInfo.getBSSID()).thenReturn(null); - when(wifiInfo.getNetworkId()).thenReturn(-1); - return wifiInfo; - } - - WifiConfigManager getWifiConfigManager() { - WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); - when(wifiConfigManager.getLastSelectedNetwork()) - .thenReturn(WifiConfiguration.INVALID_NETWORK_ID); - return wifiConfigManager; - } - - /** - * This API is used to generate multiple simulated saved configurations used for test - * - * @param ssid array of SSID of saved configuration - * @param security array of securities of saved configuration - * @return generated new array of configurations based on input - */ - private WifiConfiguration[] generateWifiConfigurations(String[] ssid, int[] security) { - if (ssid == null || security == null || ssid.length != security.length - || ssid.length == 0) { - return null; - } - - WifiConfiguration[] configs = new WifiConfiguration[ssid.length]; - for (int index = 0; index < ssid.length; index++) { - configs[index] = generateWifiConfig(index, 0, ssid[index], false, true, null, null, - security[index]); - } - - return configs; - } - - /** - * set configuration to a passpoint configuration - * - * @param config The configuration need to be set as a passipoint configuration - */ - private void setConfigPasspoint(WifiConfiguration config) { - config.FQDN = "android.qns.unitTest"; - config.providerFriendlyName = "android.qns.unitTest"; - WifiEnterpriseConfig enterpriseConfig = mock(WifiEnterpriseConfig.class); - when(enterpriseConfig.getEapMethod()).thenReturn(WifiEnterpriseConfig.Eap.PEAP); - - } - - /** - * Add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according - * to the networkd ID) and setup the WifiConfigManager mocks for these networks. - * This simulates the WifiConfigManager class behaviour. - * - * @param configs input configuration need to be added to WifiConfigureStore - */ - private void prepareConfigStore(final WifiConfiguration[] configs) { - when(mWifiConfigManager.getConfiguredNetwork(anyInt())) - .then(new AnswerWithArguments() { - public WifiConfiguration answer(int netId) { - if (netId >= 0 && netId < configs.length) { - return new WifiConfiguration(configs[netId]); - } else { - return null; - } - } - }); - when(mWifiConfigManager.getConfiguredNetwork(anyString())) - .then(new AnswerWithArguments() { - public WifiConfiguration answer(String configKey) { - for (int netId = 0; netId < configs.length; netId++) { - if (TextUtils.equals(configs[netId].configKey(), configKey)) { - return new WifiConfiguration(configs[netId]); - } - } - return null; - } - }); - when(mWifiConfigManager.getSavedNetworks()) - .then(new AnswerWithArguments() { - public List<WifiConfiguration> answer() { - List<WifiConfiguration> savedNetworks = new ArrayList<>(); - for (int netId = 0; netId < configs.length; netId++) { - savedNetworks.add(new WifiConfiguration(configs[netId])); - } - return savedNetworks; - } - }); - when(mWifiConfigManager.clearNetworkCandidateScanResult(anyInt())) - .then(new AnswerWithArguments() { - public boolean answer(int netId) { - if (netId >= 0 && netId < configs.length) { - configs[netId].getNetworkSelectionStatus().setCandidate(null); - configs[netId].getNetworkSelectionStatus() - .setCandidateScore(Integer.MIN_VALUE); - configs[netId].getNetworkSelectionStatus() - .setSeenInLastQualifiedNetworkSelection(false); - return true; - } else { - return false; - } - } - }); - when(mWifiConfigManager.setNetworkCandidateScanResult( - anyInt(), any(ScanResult.class), anyInt())) - .then(new AnswerWithArguments() { - public boolean answer(int netId, ScanResult scanResult, int score) { - if (netId >= 0 && netId < configs.length) { - configs[netId].getNetworkSelectionStatus().setCandidate(scanResult); - configs[netId].getNetworkSelectionStatus().setCandidateScore(score); - configs[netId].getNetworkSelectionStatus() - .setSeenInLastQualifiedNetworkSelection(true); - return true; - } else { - return false; - } - } - }); - when(mWifiConfigManager.clearNetworkConnectChoice(anyInt())) - .then(new AnswerWithArguments() { - public boolean answer(int netId) { - if (netId >= 0 && netId < configs.length) { - configs[netId].getNetworkSelectionStatus().setConnectChoice(null); - configs[netId].getNetworkSelectionStatus() - .setConnectChoiceTimestamp( - NetworkSelectionStatus - .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); - return true; - } else { - return false; - } - } - }); - when(mWifiConfigManager.setNetworkConnectChoice(anyInt(), anyString(), anyLong())) - .then(new AnswerWithArguments() { - public boolean answer(int netId, String configKey, long timestamp) { - if (netId >= 0 && netId < configs.length) { - configs[netId].getNetworkSelectionStatus().setConnectChoice(configKey); - configs[netId].getNetworkSelectionStatus().setConnectChoiceTimestamp( - timestamp); - return true; - } else { - return false; - } - } - }); - } - - /** - * Setup WifiConfigManager mock for the ephemeral network addition and getter/setter methods. - */ - private WifiConfiguration setupEphemeralNetwork( - int networkId, ScanResult scanResult, boolean meteredHint) { - // Return the correct networkID for ephemeral network addition. - when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) - .thenReturn(new NetworkUpdateResult(networkId)); - final WifiConfiguration config = ScanResultUtil.createNetworkFromScanResult(scanResult); - config.networkId = networkId; - config.meteredHint = meteredHint; - -// when(mWifiConfigManager.getConfiguredNetwork(networkId)) - // .thenReturn(new WifiConfiguration(config)); - - when(mWifiConfigManager.getConfiguredNetwork(eq(networkId))) - .then(new AnswerWithArguments() { - public WifiConfiguration answer(int netId) { - return new WifiConfiguration(config); - } - }); - when(mWifiConfigManager.setNetworkCandidateScanResult( - eq(networkId), any(ScanResult.class), anyInt())) - .then(new AnswerWithArguments() { - public boolean answer(int netId, ScanResult scanResult, int score) { - config.getNetworkSelectionStatus().setCandidate(scanResult); - config.getNetworkSelectionStatus().setCandidateScore(score); - config.getNetworkSelectionStatus() - .setSeenInLastQualifiedNetworkSelection(true); - return true; - } - }); - return config; - } - - /** - * Link scan results to the saved configurations. - * - * The shorter of the 2 input params will be used to loop over so the inputs don't - * need to be of equal length. If there are more scan details then configs the remaining scan - * details will be associated with a NULL config. - * - * @param configs saved configurations - * @param scanDetails come in scan results - */ - private void scanResultLinkConfiguration(WifiConfiguration[] configs, - List<ScanDetail> scanDetails) { - if (scanDetails.size() <= configs.length) { - for (int i = 0; i < scanDetails.size(); i++) { - ScanDetail scanDetail = scanDetails.get(i); - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail))) - .thenReturn(configs[i]); - } - } else { - for (int i = 0; i < configs.length; i++) { - ScanDetail scanDetail = scanDetails.get(i); - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail))) - .thenReturn(configs[i]); - } - - // associated the remaining scan details with a NULL config. - for (int i = configs.length; i < scanDetails.size(); i++) { - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache( - eq(scanDetails.get(i)))).thenReturn(null); - } - } - } - - private void configureScoreCache(List<ScanDetail> scanDetails, Integer[] scores, - boolean[] meteredHints) { - for (int i = 0; i < scanDetails.size(); i++) { - ScanDetail scanDetail = scanDetails.get(i); - Integer score = scores[i]; - ScanResult scanResult = scanDetail.getScanResult(); - if (score != null) { - when(mScoreCache.isScoredNetwork(scanResult)).thenReturn(true); - when(mScoreCache.hasScoreCurve(scanResult)).thenReturn(true); - when(mScoreCache.getNetworkScore(eq(scanResult), anyBoolean())).thenReturn(score); - when(mScoreCache.getNetworkScore(scanResult)).thenReturn(score); - } else { - when(mScoreCache.isScoredNetwork(scanResult)).thenReturn(false); - when(mScoreCache.hasScoreCurve(scanResult)).thenReturn(false); - when(mScoreCache.getNetworkScore(eq(scanResult), anyBoolean())).thenReturn( - WifiNetworkScoreCache.INVALID_NETWORK_SCORE); - when(mScoreCache.getNetworkScore(scanResult)).thenReturn( - WifiNetworkScoreCache.INVALID_NETWORK_SCORE); - } - when(mScoreCache.getMeteredHint(scanResult)).thenReturn(meteredHints[i]); - } - } - - /** - * Verify whether the chosen configuration matched with the expected chosen scan result - * - * @param chosenScanResult the expected chosen scan result - * @param chosenCandidate the chosen configuration - */ - private void verifySelectedResult( - ScanResult chosenScanResult, WifiConfiguration chosenCandidate) { - verify(mWifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult( - eq(chosenCandidate.networkId), eq(chosenScanResult), anyInt()); - } - - // QNS test under disconnected State - - /** - * Case #1 choose 2GHz stronger RSSI test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled, encrypted and at 2.4 GHz - * test1 is with RSSI -70 test2 is with RSSI -60 - * - * Expected behavior: test2 is chosen - */ - @Test - public void chooseNetworkDisconnected2GHighestRssi() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2417}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-70, -60}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual( - savedConfigs[scanDetails.size() - 1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #2 choose 5GHz Stronger RSSI Test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled, encrypted and at 5 GHz - * test1 is with RSSI -70 test2 is with RSSI -60 - * - * Expected behavior: test2 is chosen - */ - @Test - public void chooseNetworkDisconnected5GHighestRssi() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5180, 5610}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-70, -60}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual( - savedConfigs[scanDetails.size() - 1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #3 5GHz over 2GHz bonus Test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled - * test1 is @ 2GHz with RSSI -60 - * test2 is @ 5Ghz with RSSI -65 - * - * Expected behavior: test2 is chosen due to 5GHz bonus - */ - @Test - public void chooseNetworkDisconnect5GOver2GTest() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-60, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual( - savedConfigs[scanDetails.size() - 1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #4 2GHz over 5GHz dur to 5GHz signal too weak test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled - * test1 is @ 2GHz with RSSI -60 - * test2 is @ 5Ghz with RSSI -75 - * - * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not - * compensate) - */ - @Test - public void chooseNetworkDisconnect2GOver5GTest() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-60, -75}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #5 2GHz signal Saturation test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled - * test1 is @ 2GHz with RSSI -50 - * test2 is @ 5Ghz with RSSI -65 - * - * Expected behavior: test2 is chosen. Although the RSSI delta here is 15 too, because 2GHz RSSI - * saturates at -60, the real RSSI delta is only 5, which is less than 5GHz bonus - */ - @Test - public void chooseNetworkDisconnect2GRssiSaturationTest() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual( - savedConfigs[scanDetails.size() - 1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #6 Minimum RSSI test - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network - * Both network are enabled - * test1 is @ 2GHz with RSSI -86 - * test2 is @ 5Ghz with RSSI -83 - * - * Expected behavior: no QNS is made because both network are below the minimum threshold, null - */ - @Test - public void chooseNetworkMinimumRssiTest() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI - 1, - WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI - 1}; - int[] security = {SECURITY_EAP, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - assertEquals("choose the wrong SSID", null, candidate); - } - - /** - * Case #7 encrypted network over passpoint network - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1 is secured network, test2 are passpoint network - * Both network are enabled and at 2.4 GHz. Both have RSSI of -70 - * - * Expected behavior: test1 is chosen since secured network has higher priority than passpoint - * network - */ - @Test - public void chooseNetworkSecurityOverPassPoint() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] levels = {-70, -70}; - int[] security = {SECURITY_EAP, SECURITY_NONE}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - setConfigPasspoint(savedConfigs[1]); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #8 passpoint network over open network - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1 is passpoint network, test2 is open network - * Both network are enabled and at 2.4 GHz. Both have RSSI of -70 - * - * Expected behavior: test1 is chosen since passpoint network has higher priority than open - * network - */ - @Test - public void chooseNetworkPasspointOverOpen() { - String[] ssids = {"\"test1\"", "\"test2\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f8", "6c:f3:7f:ae:8c:f4"}; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-70, -70}; - int[] security = {SECURITY_NONE, SECURITY_NONE}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - setConfigPasspoint(savedConfigs[0]); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #9 secure network over open network - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * Two networks test1 is secure network, test2 is open network - * Both network are enabled and at 2.4 GHz. Both have RSSI of -70 - * - * Expected behavior: test1 is chosen since secured network has higher priority than open - * network - */ - @Test - public void chooseNetworkSecureOverOpen() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-70, -70}; - int[] security = {SECURITY_PSK, SECURITY_NONE}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #10 first time user select a network - * - * In this test. we simulate following scenario - * There are three saved networks: test1, test2 and test3. Now user select the network test3 - * check test3 has been saved in test1's and test2's ConnectChoice - * - * Expected behavior: test1's and test2's ConnectChoice should be test3, test3's ConnectChoice - * should be null - */ - @Test - public void userSelectsNetworkForFirstTime() { - String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""}; - int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_NONE}; - - final WifiConfiguration[] configs = generateWifiConfigurations(ssids, security); - for (WifiConfiguration network : configs) { - NetworkSelectionStatus status = network.getNetworkSelectionStatus(); - status.setSeenInLastQualifiedNetworkSelection(true); - } - prepareConfigStore(configs); - - mWifiQualifiedNetworkSelector.setUserConnectChoice(configs.length - 1); - String key = configs[configs.length - 1].configKey(); - for (int index = 0; index < configs.length; index++) { - WifiConfiguration config = configs[index]; - NetworkSelectionStatus status = config.getNetworkSelectionStatus(); - if (index == configs.length - 1) { - assertEquals("User selected network should not have prefernce over it", null, - status.getConnectChoice()); - } else { - assertEquals("Wrong user preference", key, status.getConnectChoice()); - } - } - } - - /** - * Case #11 choose user selected network - * - * In this test, we simulate following scenario: - * WifiStateMachine is under disconnected state - * There are three networks: test1, test2, test3 and test3 is the user preference - * All three networks are enabled - * test1 is @ 2.4GHz with RSSI -50 PSK - * test2 is @ 5Ghz with RSSI -65 PSK - * test3 is @ 2.4GHz with RSSI -55 open - * - * Expected behavior: test3 is chosen since it is user selected network. It overcome all other - * priorities - */ - @Test - public void chooseUserPreferredNetwork() { - String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""}; - int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_NONE}; - - final WifiConfiguration[] configs = generateWifiConfigurations(ssids, security); - for (WifiConfiguration network : configs) { - NetworkSelectionStatus status = network.getNetworkSelectionStatus(); - status.setSeenInLastQualifiedNetworkSelection(true); - } - prepareConfigStore(configs); - - //set user preference - mWifiQualifiedNetworkSelector.setUserConnectChoice(ssids.length - 1); - //Generate mocked recent scan results - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"}; - int[] frequencies = {2437, 5180, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]", "NONE"}; - int[] levels = {-50, -65, -55}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - scanResultLinkConfiguration(configs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual( - configs[scanDetails.size() - 1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #12 enable a blacklisted BSSID - * - * In this test, we simulate following scenario: - * For two Aps, BSSIDA and BSSIDB. Disable BSSIDA, then check whether BSSIDA is disabled and - * BSSIDB is enabled. Then enable BSSIDA, check whether both BSSIDs are enabled. - */ - @Test - public void enableBssidTest() { - String bssidA = "6c:f3:7f:ae:8c:f3"; - String bssidB = "6c:f3:7f:ae:8c:f4"; - //check by default these two BSSIDs should be enabled - assertEquals("bssidA should be enabled by default", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false); - assertEquals("bssidB should be enabled by default", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false); - - //disable bssidA 3 times, check whether A is dsiabled and B is still enabled - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false); - assertEquals("bssidA should be disabled", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false); - assertEquals("bssidA should be disabled", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false); - assertEquals("bssidA should be disabled", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), true); - assertEquals("bssidB should still be enabled", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false); - - //re-enable bssidA, check whether A is dsiabled and B is still enabled - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, true); - assertEquals("bssidA should be enabled by default", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false); - assertEquals("bssidB should be enabled by default", - mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false); - - //make sure illegal input will not cause crash - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(null, false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(null, true); - } - - /** - * Case #13 do not choose the BSSID has been disabled - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network and found in scan results - * Both network are enabled - * test1 is @ 2GHz with RSSI -65 - * test2 is @ 5Ghz with RSSI -50 - * test2's BSSID is disabled - * - * expected return test1 since test2's BSSID has been disabled - */ - @Test - public void networkChooseWithOneBssidDisabled() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-65, -50}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #14 re-choose the disabled BSSID after it is re-enabled - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network and found in scan results - * Both network are enabled - * test1 is @ 2GHz with RSSI -65 - * test2 is @ 5Ghz with RSSI -50 - * test2's BSSID is disabled - * - * expected return test2 since test2's BSSID has been enabled again - */ - @Test - public void networkChooseWithOneBssidReenaabled() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-65, -50}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - //re-enable it - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], true); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #15 re-choose the disabled BSSID after its disability has expired - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network and found in scan results - * Both network are enabled - * test1 is @ 2GHz with RSSI -65 - * test2 is @ 5Ghz with RSSI -50 - * test2's BSSID is disabled - * - * expected return test2 since test2's BSSID has been enabled again - */ - @Test - public void networkChooseWithOneBssidDisableExpire() { - String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"}; - int[] frequencies = {2437, 5180, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]", - "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-65, -50, -60}; - int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - for (int index = 0; index < WifiQualifiedNetworkSelector.BSSID_BLACKLIST_THRESHOLD; - index++) { - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false); - mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[2], false); - } - - //re-enable it - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() - + WifiQualifiedNetworkSelector.BSSID_BLACKLIST_EXPIRE_TIME_MS); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - /** - * Case #16 do not choose the SSID has been disabled - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state - * Two networks test1, test2 are secured network and found in scan results - * Both network are enabled - * test1 is @ 2GHz with RSSI -65 - * test2 is @ 5Ghz with RSSI -50 - * test2's SSID is disabled - * - * expected return test1 since test2's SSID has been disabled - */ - @Test - public void networkChooseWithOneSsidDisabled() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-65, -50}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - when(mWifiConfigManager.tryEnableNetwork(anyInt())).thenReturn(true); - savedConfigs[1].getNetworkSelectionStatus().setNetworkSelectionStatus( - NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #17 do not make QNS is link is bouncing now - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state and currently is under link bouncing - * Two networks test1, test2 are secured network and found in scan results - * Both network are enabled - * test1 is @ 2GHz with RSSI -50 - * test2 is @ 5Ghz with RSSI -50 - * - * expected return null - */ - @Test - public void noQNSWhenLinkBouncingDisconnected() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI - 1, - WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI - 1}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, true, false, true, false, scanDetails); - - assertEquals("choose the wrong network", null, candidate); - } - - /** - * Case #18 QNS with very short gap - * - * In this test. we simulate following scenario: - * WifiStateMachine is under disconnected state - * If last QNS is made in less than MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, we - * still should make new QNS since it is disconnected now - * - * expect return test1 because of band bonus - */ - @Test - public void networkSelectionInShortGap() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - //first QNS - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - //immediately second QNS - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - //Unit test for Connected State - - /** - * Case #19 no QNS with very short gap when connected - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and test2 is connected - * When WifiStateMachine is already in connected state, if last QNS is made in less than - * MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, no new QNS should be made - * - * expect return NULL - */ - @Test - public void noNetworkSelectionDueToShortGap() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - //first QNS - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - //immediately second QNS - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - assertEquals("choose the wrong BSSID", null, candidate); - } - - /** - * Case #20 force QNS with very short gap under connection - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and test2 is connected - * When WifiStateMachine is already in connected state, if last QNS is made in less than - * MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, no new QNS should be made. However, if we force - * to make new QNS, QNS still will be made - * - * expect return test2 since it is the current connected one (bonus) - */ - @Test - public void forceNetworkSelectionInShortGap() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - //first QNS - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - //immediately second QNS - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true, - false, false, true, false, false, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #21 no QNS when connected and user do not allow switch when connected - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and test2 is connected - * if user does not allow switch network when connected, do not make new QNS when connected - * - * expect return NULL - */ - @Test - public void noNewNetworkSelectionDuetoUserDisableSwitchWhenConnected() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - assertEquals("choose the wrong BSSID", null, candidate); - assertEquals("Should receive zero filteredScanDetails", 0, - mWifiQualifiedNetworkSelector.getFilteredScanDetails().size()); - } - - /** - * Case #22 no new QNS if current network is qualified already - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and test2 is connected - * If current connected network is Qualified already, do not make new QNS - * simulated current connected network as: - * 5GHz, RSSI = WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND, secured - * - * expected return null - */ - @Test - public void noNewQNSCurrentNetworkQualified() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-65, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - //first time, connect to test2 due to 5GHz bonus - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - - levels[0] = -50; // if there is QNS, test1 will be chosen - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - assertEquals("choose the wrong BSSID", null, candidate); - } - - /** - * Case #23 No new QNS when link bouncing when connected - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and test2 is connected - * no new QNS when link is bouncing - * - * expected return null - */ - @Test - public void noNewQNSLinkBouncing() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-70, -75}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - //first connect to test2 due to 5GHz bonus - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, true, true, false, false, scanDetails); - assertEquals("choose the wrong BSSID", null, candidate); - } - - /** - * Case #24 Qualified network need to be on 5GHz - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and connected to test2 - * if current connected network is not 5GHz, then it is not qualified. We should make new QNS - * - * expected result: return test1 - */ - @Test - public void currentNetworkNotQualifiedDueToBandMismatch() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-50, -65}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(0); - when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); - when(mWifiInfo.is24GHz()).thenReturn(true); - //connect to config2 first - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #25 Qualified network need to be secured - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connects to test2. If current connected - * network is open network, then it is not qualified. We should make new QNS selection and - * ensure that we switch to the secure one if the RSSI is sufficiently high. - * - * expected result: return test1 since test1 has higher RSSI - */ - @Test - public void currentNetworkNotQualifiedDueToOpenNetwork() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5400, 5400}; - String[] caps = {"[WPA2-EAP-CCMP][ESS][ESS]", "[ESS]"}; - int[] levels = {-80, -45}; - int[] security = {SECURITY_PSK, SECURITY_NONE}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - //first connect to test2 because of RSSI - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - when(mWifiInfo.is5GHz()).thenReturn(true); - when(mClock.getElapsedSinceBootMillis()) - .thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - - // Now increase RSSI of test1 - levels[0] = -60; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #26 ephemeral network can not be qualified network - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * if current connected network is ephemeral network, then it is not qualified. We should make - * new QNS - * - * expected result: return test1 (since test2 is ephemeral) - */ - @Test - public void currentNetworkNotQualifiedDueToEphemeral() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, -50}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - savedConfigs[1].ephemeral = true; - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - //first connect to test2 since test1's RSSI is negligible - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - - levels[0] = -70; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - scanResultLinkConfiguration(savedConfigs, scanDetails); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #27 low signal network can not be Qualified network (5GHz) - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * if current connected network's rssi is too low, then it is not qualified. We should - * make new QNS - * - * expected result: return test1 - */ - @Test - public void currentNetworkNotQualifiedDueToLow5GRssi() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-80, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND - 1}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.getRssi()).thenReturn(levels[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - when(mWifiInfo.is5GHz()).thenReturn(true); - - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - levels[0] = -60; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #28 low signal network can not be Qualified network (2.4GHz) - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * if current connected network's rssi is too low, then it is not qualified. We should - * make new QNS - * - * expected result: return test1 - */ - @Test - public void currentNetworkNotQualifiedDueToLow2GRssi() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND - 1}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.getRssi()).thenReturn(levels[1]); - when(mWifiInfo.is24GHz()).thenReturn(false); - when(mWifiInfo.is5GHz()).thenReturn(true); - - levels[0] = -60; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #29 Choose current network due to current network bonus - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * To connect to a network which is not linked to current connected network, unless this network - * is more than 10 db higher than current network, we should not switch. So although test2 has a - * lower signal, we still choose test2 - * - * expected result: return test2 - */ - @Test - public void currentNetworkStayDueToSameNetworkBonus() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, -80}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(true); - - levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4 - + WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 - 1; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #30 choose another network due to current network's signal is too low - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * To connect to a network which is not linked to current connected network, if this network - * is more than 10 db higher than current network, we should switch - * - * expected new result: return test1 - */ - @Test - public void switchNetworkStayDueToCurrentNetworkRssiLow() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, -80}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(true); - - levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4 - + WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 + 1; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #31 Choose current BSSID due to current BSSID bonus - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * Linked network will be treated as same network. To connect to a network which is linked to - * current connected network, unless this network is more than 6 db higher than current network, - * we should not switch AP and stick to current BSSID - * - * expected result: return test2 - */ - @Test - public void currentBssidStayDueToSameBSSIDBonus() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, -80}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - //link two configuration - savedConfigs[0].linkedConfigurations = new HashMap<String, Integer>(); - savedConfigs[1].linkedConfigurations = new HashMap<String, Integer>(); - savedConfigs[0].linkedConfigurations.put(savedConfigs[1].configKey(), 1); - savedConfigs[1].linkedConfigurations.put(savedConfigs[0].configKey(), 1); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(true); - - levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 - 1; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #32 Choose another BSSID due to current BSSID's rssi is too low - * - * In this test. we simulate following scenario: - * WifiStateMachine is under connected state and current connected to test2 - * Linked network will be treated as same network. To connect to a network which is linked to - * current connected network, unless this network is more than 6 db higher than current network, - * we should not switch AP and stick to current BSSID - * - * expected result: return test2 - */ - @Test - public void swithBssidDueToLowRssi() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"}; - int[] levels = {-100, -80}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - //link two configuration - savedConfigs[0].linkedConfigurations = new HashMap<String, Integer>(); - savedConfigs[1].linkedConfigurations = new HashMap<String, Integer>(); - savedConfigs[0].linkedConfigurations.put(savedConfigs[1].configKey(), 1); - savedConfigs[1].linkedConfigurations.put(savedConfigs[0].configKey(), 1); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false, - false, true, false, scanDetails); - - when(mWifiInfo.getNetworkId()).thenReturn(1); - when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); - when(mWifiInfo.is24GHz()).thenReturn(true); - - levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4 + 1; - scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, true, false, false, scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #33 Choose an ephemeral network with a good score because no saved networks - * are available. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is an enterprise network w/o a score. - * test2 is an open network with a good score. Additionally it's a metered network. - * isUntrustedConnectionsAllowed is set to true. - * - * expected result: return test2 with meteredHint set to True. - */ - @Test - public void selectQualifiedNetworkChoosesEphemeral() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] levels = {-70, -70}; - Integer[] scores = {null, 120}; - boolean[] meteredHints = {false, true}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - // No saved networks. - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class))) - .thenReturn(null); - - ScanResult untrustedScanResult = scanDetails.get(1).getScanResult(); - WifiConfiguration unTrustedNetworkCandidate = - setupEphemeralNetwork(1, untrustedScanResult, meteredHints[1]); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - true /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(unTrustedNetworkCandidate, candidate); - verifySelectedResult(untrustedScanResult, candidate); - assertEquals(meteredHints[1], candidate.meteredHint); - } - - /** - * Case #34 Test Filtering of potential candidate scanDetails (Untrusted allowed) - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * test1 is @ 2GHz with RSSI -60 - * test2 is @ 5Ghz with RSSI -86, (below minimum threshold) - * test3 is @ 5Ghz with RSSI -50, however it has no associated saved config - * test4 is @ 2Ghz with RSSI -62, no associated config, but is Ephemeral - * - * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not - * compensate). - * test1 & test4's scanDetails are returned by 'getFilteredScanDetail()' - */ - @Test - public void testGetFilteredScanDetailsReturnsOnlyConsideredScanDetails_untrustedAllowed() { - String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55", - "c0:ff:ee:ee:e3:ee"}; - int[] frequencies = {2437, 5180, 5180, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", - "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {-60, -86, -50, -62}; - int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK, SECURITY_PSK}; - boolean[] meteredHints = {false, false, false, true}; - Integer[] scores = {null, null, null, 120}; - - //Create all 4 scanDetails - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - - //Setup NetworkScoreCache for detecting ephemeral networks ("test4") - configureScoreCache(scanDetails, scores, meteredHints); - ScanResult untrustedScanResult = scanDetails.get(3).getScanResult(); - - //Set up associated configs for test1 & test2 - WifiConfiguration[] savedConfigs = generateWifiConfigurations( - Arrays.copyOfRange(ssids, 0, 2), Arrays.copyOfRange(security, 0, 2)); - prepareConfigStore(savedConfigs); - List<ScanDetail> savedScanDetails = new ArrayList<ScanDetail>(scanDetails.subList(0, 2)); - scanResultLinkConfiguration(savedConfigs, savedScanDetails); - - setupEphemeralNetwork(2, scanDetails.get(2).getScanResult(), meteredHints[2]); - setupEphemeralNetwork(3, scanDetails.get(3).getScanResult(), meteredHints[3]); - - //Force mock ConfigManager to return null (and not an empty list) for "test3" & "test4" - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetails.get(2)))) - .thenReturn(null); - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetails.get(3)))) - .thenReturn(null); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - true /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - - verifySelectedResult(chosenScanResult, candidate); - //Verify two scanDetails returned in the filteredScanDetails - assertEquals(2, mWifiQualifiedNetworkSelector.getFilteredScanDetails().size()); - assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(0).first.toString(), - scanDetails.get(0).toString()); - assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(1).first.toString(), - scanDetails.get(3).toString()); - } - - - /** - * Case #35 Test Filtering of potential candidate scanDetails (Untrusted disallowed) - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * test1 is @ 2GHz with RSSI -60 - * test2 is @ 5Ghz with RSSI -86, (below minimum threshold) - * test3 is @ 5Ghz with RSSI -50, however it has no associated saved config - * test4 is @ 2Ghz with RSSI -62, no associated config, but is Ephemeral - * - * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not - * compensate). - * test1 & test4's scanDetails are returned by 'getFilteredScanDetail()' - */ - @Test - public void testGetFilteredScanDetailsReturnsOnlyConsideredScanDetails_untrustedDisallowed() { - String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55", - "c0:ff:ee:ee:e3:ee"}; - int[] frequencies = {2437, 5180, 5180, 2437}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", - "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {-60, -86, -50, -62}; - int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK, SECURITY_PSK}; - boolean[] meteredHints = {false, false, false, true}; - Integer[] scores = {null, null, null, 120}; - - //Create all 4 scanDetails - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - - //Setup NetworkScoreCache for detecting ephemeral networks ("test4") - configureScoreCache(scanDetails, scores, meteredHints); - ScanResult untrustedScanResult = scanDetails.get(3).getScanResult(); - WifiConfiguration unTrustedNetworkCandidate = - ScanResultUtil.createNetworkFromScanResult(untrustedScanResult); - - //Set up associated configs for test1 & test2 - WifiConfiguration[] savedConfigs = generateWifiConfigurations( - Arrays.copyOfRange(ssids, 0, 2), Arrays.copyOfRange(security, 0, 2)); - prepareConfigStore(savedConfigs); - List<ScanDetail> savedScanDetails = new ArrayList<ScanDetail>(scanDetails.subList(0, 2)); - scanResultLinkConfiguration(savedConfigs, savedScanDetails); - - setupEphemeralNetwork(2, scanDetails.get(2).getScanResult(), meteredHints[2]); - setupEphemeralNetwork(3, scanDetails.get(3).getScanResult(), meteredHints[3]); - - //Force mock ConfigManager to return null (and not an empty list) for "test3" & "test4" - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetails.get(2)))) - .thenReturn(null); - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetails.get(3)))) - .thenReturn(null); - - ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - false /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - - verifySelectedResult(chosenScanResult, candidate); - //Verify two scanDetails returned in the filteredScanDetails - assertEquals(1, mWifiQualifiedNetworkSelector.getFilteredScanDetails().size()); - assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(0).first.toString(), - scanDetails.get(0).toString()); - } - - /** - * Case #36 Ignore an ephemeral network if it was previously deleted. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is an open network with a low score. Additionally it's a metered network. - * test2 is an open network with a good score but was previously deleted. - * isUntrustedConnectionsAllowed is set to true. - * - * expected result: return test1 with meteredHint set to True. - */ - @Test - public void selectQualifiedNetworkDoesNotChooseDeletedEphemeral() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] levels = {-70, -70}; - Integer[] scores = {20, 120}; - boolean[] meteredHints = {true, false}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - // No saved networks. - when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class))) - .thenReturn(null); - - ScanResult untrustedScanResult = scanDetails.get(0).getScanResult(); - WifiConfiguration unTrustedNetworkCandidate = - setupEphemeralNetwork(0, untrustedScanResult, meteredHints[0]); - - // The second scan result is for an ephemeral network which was previously deleted - when(mWifiConfigManager - .wasEphemeralNetworkDeleted(scanDetails.get(0).getScanResult().SSID)) - .thenReturn(false); - when(mWifiConfigManager - .wasEphemeralNetworkDeleted(scanDetails.get(1).getScanResult().SSID)) - .thenReturn(true); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - true /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(unTrustedNetworkCandidate, candidate); - verifySelectedResult(untrustedScanResult, candidate); - assertEquals(meteredHints[0], candidate.meteredHint); - } - - /** - * Case #37 Choose the saved config that doesn't qualify for external scoring. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is a saved network. - * test2 is a saved network with useExternalScores set to true and a very high score. - * - * expected result: return test1 because saved networks that don't request external scoring - * have a higher priority. - */ - @Test - public void selectQualifiedNetworkPrefersSavedWithoutExternalScores() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - int[] levels = {-70, -70}; - Integer[] scores = {null, 120}; - boolean[] meteredHints = {false, true}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - WifiConfiguration[] savedConfigs = generateWifiConfigurations(DEFAULT_SSIDS, security); - savedConfigs[1].useExternalScores = true; // test2 is set to use external scores. - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - false /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(scanDetails.get(0).getScanResult(), candidate); - } - - /** - * Case #38 Choose the saved config that does qualify for external scoring when other saved - * networks are not available. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is a saved network with useExternalScores set to true and a very high score. - * test2 is a saved network but not in range (not included in the scan results). - * - * expected result: return test1 because there are no better saved networks within range. - */ - @Test - public void selectQualifiedNetworkSelectsSavedWithExternalScores() { - String[] ssids = {"\"test1\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3"}; - int[] frequencies = {5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - int[] levels = {-70}; - Integer[] scores = {120}; - boolean[] meteredHints = {false}; - - // Scan details only contains 1 ssid, test1. - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - // The saved config contains 2 ssids, test1 & test2. - WifiConfiguration[] savedConfigs = generateWifiConfigurations(DEFAULT_SSIDS, security); - savedConfigs[0].useExternalScores = true; // test1 is set to use external scores. - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - false /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(scanDetails.get(0).getScanResult(), candidate); - } - - /** - * Case #39 Choose the saved config that does qualify for external scoring over the - * untrusted network with the same score. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is a saved network with useExternalScores set to true and the same score as test2. - * test2 is NOT saved network but in range with a good external score. - * - * expected result: return test1 because the tie goes to the saved network. - */ - @Test - public void selectQualifiedNetworkPrefersSavedWithExternalScoresOverUntrusted() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - int[] levels = {-70, -70}; - Integer[] scores = {120, 120}; - boolean[] meteredHints = {false, true}; - - // Both networks are in the scan results. - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - // Set up the associated configs only for test1 - WifiConfiguration[] savedConfigs = generateWifiConfigurations( - Arrays.copyOfRange(ssids, 0, 1), Arrays.copyOfRange(security, 0, 1)); - savedConfigs[0].useExternalScores = true; // test1 is set to use external scores. - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - setupEphemeralNetwork(1, scanDetails.get(1).getScanResult(), meteredHints[1]); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - true /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); - verifySelectedResult(scanDetails.get(0).getScanResult(), candidate); - } - - /** - * Case #40 Choose the ephemeral config over the saved config that does qualify for external - * scoring because the untrusted network has a higher score. - * - * In this test. we simulate following scenario: - * WifiStateMachine is not connected to any network. - * selectQualifiedNetwork() is called with 2 scan results, test1 and test2. - * test1 is a saved network with useExternalScores set to true and a low score. - * test2 is NOT saved network but in range with a good external score. - * - * expected result: return test2 because it has a better score. - */ - @Test - public void selectQualifiedNetworkPrefersUntrustedOverScoredSaved() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {5200, 5200}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - int[] levels = {-70, -70}; - Integer[] scores = {10, 120}; - boolean[] meteredHints = {false, true}; - - // Both networks are in the scan results. - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - configureScoreCache(scanDetails, scores, meteredHints); - - // Set up the associated configs only for test1 - WifiConfiguration[] savedConfigs = generateWifiConfigurations( - Arrays.copyOfRange(ssids, 0, 1), Arrays.copyOfRange(security, 0, 1)); - savedConfigs[0].useExternalScores = true; // test1 is set to use external scores. - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult untrustedScanResult = scanDetails.get(1).getScanResult(); - WifiConfiguration unTrustedNetworkCandidate = - setupEphemeralNetwork(1, untrustedScanResult, meteredHints[1]); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork( - false /* forceSelectNetwork */, - true /* isUntrustedConnectionsAllowed */, - false, /* isLinkDebouncing */ - false, /* isConnected */ - true, /* isDisconnected */ - false, /* isSupplicantTransient */ - scanDetails); - WifiConfigurationTestUtil.assertConfigurationEqual(unTrustedNetworkCandidate, candidate); - verifySelectedResult(untrustedScanResult, candidate); - } - - - /** - * Case #41 Ensure the ExternalScoreEvaluator correctly selects the untrusted network. - * - * In this test. we simulate following scenario: - * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network. - * The untrusted network has the higher score. - * - * expected result: The untrusted network is determined to be the best network. - */ - @Test - public void externalScoreEvaluator_untrustedIsBest() { - WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator = - new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true); - ScanResult untrustedScanResult = new ScanResult(); - int untrustedScore = 100; - evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult); - - ScanResult savedScanResult = new ScanResult(); - int savedScore = 50; - WifiConfiguration savedConfig = new WifiConfiguration(); - evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.UNTRUSTED_NETWORK, evaluator.getBestCandidateType()); - assertEquals(untrustedScore, evaluator.getHighScore()); - assertSame(untrustedScanResult, evaluator.getScanResultCandidate()); - } - - /** - * Case #42 Ensure the ExternalScoreEvaluator correctly selects the saved network. - * - * In this test. we simulate following scenario: - * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network. - * The saved network has the higher score. - * - * expected result: The saved network is determined to be the best network. - */ - @Test - public void externalScoreEvaluator_savedIsBest() { - WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator = - new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true); - ScanResult untrustedScanResult = new ScanResult(); - int untrustedScore = 50; - evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult); - - ScanResult savedScanResult = new ScanResult(); - int savedScore = 100; - WifiConfiguration savedConfig = new WifiConfiguration(); - evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType()); - assertEquals(savedScore, evaluator.getHighScore()); - assertSame(savedScanResult, evaluator.getScanResultCandidate()); - } - - /** - * Case #43 Ensure the ExternalScoreEvaluator correctly selects the saved network if a - * tie occurs. - * - * In this test. we simulate following scenario: - * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network. - * Both networks have the same score. - * - * expected result: The saved network is determined to be the best network. - */ - @Test - public void externalScoreEvaluator_tieScores() { - WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator = - new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true); - ScanResult untrustedScanResult = new ScanResult(); - int untrustedScore = 100; - evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult); - - ScanResult savedScanResult = new ScanResult(); - int savedScore = 100; - WifiConfiguration savedConfig = new WifiConfiguration(); - evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType()); - assertEquals(savedScore, evaluator.getHighScore()); - assertSame(savedScanResult, evaluator.getScanResultCandidate()); - } - - /** - * Case #44 Ensure the ExternalScoreEvaluator correctly selects the saved network out of - * multiple options. - * - * In this test. we simulate following scenario: - * The ExternalScoreEvaluator is asked to evaluate 2 untrusted networks and 2 saved networks. - * The high scores are equal and the low scores differ. - * - * expected result: The saved network is determined to be the best network. - */ - @Test - public void externalScoreEvaluator_multipleScores() { - WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator = - new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true); - ScanResult untrustedScanResult = new ScanResult(); - int untrustedScore = 100; - evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult); - evaluator.evalUntrustedCandidate(80, new ScanResult()); - - ScanResult savedScanResult = new ScanResult(); - int savedScore = 100; - WifiConfiguration savedConfig = new WifiConfiguration(); - evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult); - evaluator.evalSavedCandidate(90, new WifiConfiguration(), new ScanResult()); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType()); - assertEquals(savedScore, evaluator.getHighScore()); - assertSame(savedScanResult, evaluator.getScanResultCandidate()); - } - - /** - * Case #45 Ensure the ExternalScoreEvaluator correctly handles NULL score inputs. - * - * In this test we simulate following scenario: - * The ExternalScoreEvaluator is asked to evaluate both types of candidates with NULL scores. - * - * expected result: No crashes. The best candidate type is returned as NONE. - */ - @Test - public void externalScoreEvaluator_nullScores() { - WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator = - new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true); - evaluator.evalUntrustedCandidate(null, new ScanResult()); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.NONE, evaluator.getBestCandidateType()); - evaluator.evalSavedCandidate(null, new WifiConfiguration(), new ScanResult()); - assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator - .BestCandidateType.NONE, evaluator.getBestCandidateType()); - } - - /** - * Case #48 5GHz due to user band preference is set to AUTO - * - * In this test. we simulate following scenario - * WifiStateMachine is under disconnected state - * User band preference is set to AUTO - * Two networks test1, test2 are secured network - * Both network are enabled - * test1 is @ 2GHz with RSSI -50 - * test2 is @ 5Ghz with RSSI -50 - * - * Expected behavior: test2 is chosen due to 5G bonus and user band preference - * is set to AUTO. - */ - @Test - public void chooseNetworkDisconnectUserPreferAutoBandTest() { - String[] ssids = DEFAULT_SSIDS; - String[] bssids = DEFAULT_BSSIDS; - int[] frequencies = {2437, 5180}; - String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; - int[] levels = {-50, -50}; - int[] security = {SECURITY_PSK, SECURITY_PSK}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - - scanResultLinkConfiguration(savedConfigs, scanDetails); - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - verifySelectedResult(chosenScanResult, candidate); - } - - /** - * Case #49 Choose 2.4GHz BSSID with stronger RSSI value over - * 5GHz BSSID with weaker RSSI value - * - * In this test. we simulate following scenario: - * Two APs are found in scan results - * BSSID1 is @ 5GHz with RSSI -82 - * BSSID2 is @ 2Ghz with RSSI -72 - * These two BSSIDs get exactly the same QNS score - * - * expect BSSID2 to be chosen as it has stronger RSSI value - */ - @Test - public void chooseStrongerRssiOver5GHz() { - String[] ssids = {"\"test1\"", "\"test1\""}; - String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; - int[] frequencies = {5220, 2437}; - String[] caps = {"[ESS]", "[ESS]"}; - int[] levels = {-82, -72}; - int[] security = {SECURITY_NONE, SECURITY_NONE}; - - List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); - WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); - prepareConfigStore(savedConfigs); - scanResultLinkConfiguration(savedConfigs, scanDetails); - - ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); - - WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, - false, false, false, true, false, scanDetails); - - verifySelectedResult(chosenScanResult, candidate); - } -} |