1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.WifiConfiguration; 23 import android.telephony.SubscriptionManager; 24 import android.util.LocalLog; 25 26 import com.android.internal.R; 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.server.wifi.util.TelephonyUtil; 29 30 import java.util.List; 31 32 /** 33 * This class is the WifiNetworkSelector.NetworkEvaluator implementation for 34 * saved networks. 35 */ 36 public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { 37 private static final String NAME = "SavedNetworkEvaluator"; 38 private final WifiConfigManager mWifiConfigManager; 39 private final Clock mClock; 40 private final LocalLog mLocalLog; 41 private final WifiConnectivityHelper mConnectivityHelper; 42 private final SubscriptionManager mSubscriptionManager; 43 private final int mRssiScoreSlope; 44 private final int mRssiScoreOffset; 45 private final int mSameBssidAward; 46 private final int mSameNetworkAward; 47 private final int mBand5GHzAward; 48 private final int mLastSelectionAward; 49 private final int mSecurityAward; 50 private final ScoringParams mScoringParams; 51 52 /** 53 * Time it takes for the mLastSelectionAward to decay by one point, in milliseconds 54 */ 55 @VisibleForTesting 56 public static final int LAST_SELECTION_AWARD_DECAY_MSEC = 60 * 1000; 57 58 SavedNetworkEvaluator(final Context context, ScoringParams scoringParams, WifiConfigManager configManager, Clock clock, LocalLog localLog, WifiConnectivityHelper connectivityHelper, SubscriptionManager subscriptionManager)59 SavedNetworkEvaluator(final Context context, ScoringParams scoringParams, 60 WifiConfigManager configManager, Clock clock, 61 LocalLog localLog, WifiConnectivityHelper connectivityHelper, 62 SubscriptionManager subscriptionManager) { 63 mScoringParams = scoringParams; 64 mWifiConfigManager = configManager; 65 mClock = clock; 66 mLocalLog = localLog; 67 mConnectivityHelper = connectivityHelper; 68 mSubscriptionManager = subscriptionManager; 69 70 mRssiScoreSlope = context.getResources().getInteger( 71 R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); 72 mRssiScoreOffset = context.getResources().getInteger( 73 R.integer.config_wifi_framework_RSSI_SCORE_OFFSET); 74 mSameBssidAward = context.getResources().getInteger( 75 R.integer.config_wifi_framework_SAME_BSSID_AWARD); 76 mSameNetworkAward = context.getResources().getInteger( 77 R.integer.config_wifi_framework_current_network_boost); 78 mLastSelectionAward = context.getResources().getInteger( 79 R.integer.config_wifi_framework_LAST_SELECTION_AWARD); 80 mSecurityAward = context.getResources().getInteger( 81 R.integer.config_wifi_framework_SECURITY_AWARD); 82 mBand5GHzAward = context.getResources().getInteger( 83 R.integer.config_wifi_framework_5GHz_preference_boost_factor); 84 } 85 localLog(String log)86 private void localLog(String log) { 87 mLocalLog.log(log); 88 } 89 90 /** 91 * Get the evaluator type. 92 */ 93 @Override getId()94 public @EvaluatorId int getId() { 95 return EVALUATOR_ID_SAVED; 96 } 97 98 /** 99 * Get the evaluator name. 100 */ 101 @Override getName()102 public String getName() { 103 return NAME; 104 } 105 106 /** 107 * Update the evaluator. 108 */ 109 @Override update(List<ScanDetail> scanDetails)110 public void update(List<ScanDetail> scanDetails) { } 111 calculateBssidScore(ScanResult scanResult, WifiConfiguration network, WifiConfiguration currentNetwork, String currentBssid, StringBuffer sbuf)112 private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network, 113 WifiConfiguration currentNetwork, String currentBssid, 114 StringBuffer sbuf) { 115 int score = 0; 116 boolean is5GHz = scanResult.is5GHz(); 117 118 sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID) 119 .append(" RSSI:").append(scanResult.level).append(" ] "); 120 // Calculate the RSSI score. 121 int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency); 122 int rssi = Math.min(scanResult.level, rssiSaturationThreshold); 123 score += (rssi + mRssiScoreOffset) * mRssiScoreSlope; 124 sbuf.append(" RSSI score: ").append(score).append(","); 125 126 // 5GHz band bonus. 127 if (is5GHz) { 128 score += mBand5GHzAward; 129 sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(","); 130 } 131 132 // Last user selection award. 133 int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork(); 134 if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID 135 && lastUserSelectedNetworkId == network.networkId) { 136 long timeDifference = mClock.getElapsedSinceBootMillis() 137 - mWifiConfigManager.getLastSelectedTimeStamp(); 138 if (timeDifference > 0) { 139 int decay = (int) (timeDifference / LAST_SELECTION_AWARD_DECAY_MSEC); 140 int bonus = Math.max(mLastSelectionAward - decay, 0); 141 score += bonus; 142 sbuf.append(" User selection ").append(timeDifference) 143 .append(" ms ago, bonus: ").append(bonus).append(","); 144 } 145 } 146 147 // Same network award. 148 if (currentNetwork != null && network.networkId == currentNetwork.networkId) { 149 score += mSameNetworkAward; 150 sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(","); 151 152 // When firmware roaming is supported, equivalent BSSIDs (the ones under the 153 // same network as the currently connected one) get the same BSSID award. 154 if (mConnectivityHelper.isFirmwareRoamingSupported() 155 && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) { 156 score += mSameBssidAward; 157 sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(","); 158 } 159 } 160 161 // Same BSSID award. 162 if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) { 163 score += mSameBssidAward; 164 sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(","); 165 } 166 167 // Security award. 168 if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) { 169 score += mSecurityAward; 170 sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(","); 171 } 172 173 sbuf.append(" ## Total score: ").append(score).append("\n"); 174 175 return score; 176 } 177 178 /** 179 * Evaluate all the networks from the scan results and return 180 * the WifiConfiguration of the network chosen for connection. 181 * 182 * @return configuration of the chosen network; 183 * null if no network in this category is available. 184 */ 185 @Override evaluateNetworks(List<ScanDetail> scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, @NonNull OnConnectableListener onConnectableListener)186 public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, 187 WifiConfiguration currentNetwork, String currentBssid, boolean connected, 188 boolean untrustedNetworkAllowed, 189 @NonNull OnConnectableListener onConnectableListener) { 190 int highestScore = Integer.MIN_VALUE; 191 ScanResult scanResultCandidate = null; 192 WifiConfiguration candidate = null; 193 StringBuffer scoreHistory = new StringBuffer(); 194 195 for (ScanDetail scanDetail : scanDetails) { 196 ScanResult scanResult = scanDetail.getScanResult(); 197 198 // One ScanResult can be associated with more than one network, hence we calculate all 199 // the scores and use the highest one as the ScanResult's score. 200 // TODO(b/112196799): this has side effects, rather not do that in an evaluator 201 WifiConfiguration network = 202 mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail); 203 204 if (network == null) { 205 continue; 206 } 207 208 /** 209 * Ignore Passpoint and Ephemeral networks. They are configured networks, 210 * but without being persisted to the storage. They are evaluated by 211 * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator} 212 * respectively. 213 */ 214 if (network.isPasspoint() || network.isEphemeral()) { 215 continue; 216 } 217 218 WifiConfiguration.NetworkSelectionStatus status = 219 network.getNetworkSelectionStatus(); 220 // TODO (b/112196799): another side effect 221 status.setSeenInLastQualifiedNetworkSelection(true); 222 223 if (!status.isNetworkEnabled()) { 224 continue; 225 } else if (network.BSSID != null && !network.BSSID.equals("any") 226 && !network.BSSID.equals(scanResult.BSSID)) { 227 // App has specified the only BSSID to connect for this 228 // configuration. So only the matching ScanResult can be a candidate. 229 localLog("Network " + WifiNetworkSelector.toNetworkString(network) 230 + " has specified BSSID " + network.BSSID + ". Skip " 231 + scanResult.BSSID); 232 continue; 233 } else if (TelephonyUtil.isSimConfig(network) 234 && !TelephonyUtil.isSimPresent(mSubscriptionManager)) { 235 // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present. 236 continue; 237 } 238 239 int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid, 240 scoreHistory); 241 242 // Set candidate ScanResult for all saved networks to ensure that users can 243 // override network selection. See WifiNetworkSelector#setUserConnectChoice. 244 if (score > status.getCandidateScore() 245 || (score == status.getCandidateScore() 246 && status.getCandidate() != null 247 && scanResult.level > status.getCandidate().level)) { 248 mWifiConfigManager.setNetworkCandidateScanResult( 249 network.networkId, scanResult, score); 250 } 251 252 // If the network is marked to use external scores, or is an open network with 253 // curate saved open networks enabled, do not consider it for network selection. 254 if (network.useExternalScores) { 255 localLog("Network " + WifiNetworkSelector.toNetworkString(network) 256 + " has external score."); 257 continue; 258 } 259 260 onConnectableListener.onConnectable(scanDetail, 261 mWifiConfigManager.getConfiguredNetwork(network.networkId), score); 262 263 // TODO(b/112196799) - pull into common code 264 if (score > highestScore 265 || (score == highestScore 266 && scanResultCandidate != null 267 && scanResult.level > scanResultCandidate.level)) { 268 highestScore = score; 269 scanResultCandidate = scanResult; 270 mWifiConfigManager.setNetworkCandidateScanResult( 271 network.networkId, scanResultCandidate, highestScore); 272 // Reload the network config with the updated info. 273 candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId); 274 } 275 } 276 277 if (scoreHistory.length() > 0) { 278 localLog("\n" + scoreHistory.toString()); 279 } 280 281 if (scanResultCandidate == null) { 282 localLog("did not see any good candidates."); 283 } 284 return candidate; 285 } 286 } 287