SavedNetworkEvaluator.java revision 03c23584f072aef576736044c1fa12ddcb2d882b
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 17package com.android.server.wifi; 18 19import android.content.Context; 20import android.net.wifi.ScanResult; 21import android.net.wifi.WifiConfiguration; 22import android.util.LocalLog; 23import android.util.Pair; 24 25import com.android.internal.R; 26import com.android.server.wifi.util.TelephonyUtil; 27 28import java.util.ArrayList; 29import java.util.Arrays; 30import java.util.List; 31 32/** 33 * This class is the WifiNetworkSelector.NetworkEvaluator implementation for 34 * saved networks. 35 */ 36public 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 int mRssiScoreSlope; 43 private final int mRssiScoreOffset; 44 private final int mSameBssidAward; 45 private final int mSameNetworkAward; 46 private final int mBand5GHzAward; 47 private final int mLastSelectionAward; 48 private final int mSecurityAward; 49 private final int mThresholdSaturatedRssi24; 50 private final int mThresholdSaturatedRssi5; 51 52 SavedNetworkEvaluator(final Context context, WifiConfigManager configManager, Clock clock, 53 LocalLog localLog, WifiConnectivityHelper connectivityHelper) { 54 mWifiConfigManager = configManager; 55 mClock = clock; 56 mLocalLog = localLog; 57 mConnectivityHelper = connectivityHelper; 58 59 mRssiScoreSlope = context.getResources().getInteger( 60 R.integer.config_wifi_framework_RSSI_SCORE_SLOPE); 61 mRssiScoreOffset = context.getResources().getInteger( 62 R.integer.config_wifi_framework_RSSI_SCORE_OFFSET); 63 mSameBssidAward = context.getResources().getInteger( 64 R.integer.config_wifi_framework_SAME_BSSID_AWARD); 65 mSameNetworkAward = context.getResources().getInteger( 66 R.integer.config_wifi_framework_current_network_boost); 67 mLastSelectionAward = context.getResources().getInteger( 68 R.integer.config_wifi_framework_LAST_SELECTION_AWARD); 69 mSecurityAward = context.getResources().getInteger( 70 R.integer.config_wifi_framework_SECURITY_AWARD); 71 mBand5GHzAward = context.getResources().getInteger( 72 R.integer.config_wifi_framework_5GHz_preference_boost_factor); 73 mThresholdSaturatedRssi24 = context.getResources().getInteger( 74 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); 75 mThresholdSaturatedRssi5 = context.getResources().getInteger( 76 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz); 77 } 78 79 private void localLog(String log) { 80 mLocalLog.log(log); 81 } 82 83 /** 84 * Get the evaluator name. 85 */ 86 public String getName() { 87 return NAME; 88 } 89 90 /** 91 * Update all the saved networks' selection status 92 */ 93 private void updateSavedNetworkSelectionStatus() { 94 List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks(); 95 if (savedNetworks.size() == 0) { 96 localLog("No saved networks."); 97 return; 98 } 99 100 StringBuffer sbuf = new StringBuffer(); 101 for (WifiConfiguration network : savedNetworks) { 102 /** 103 * Ignore Passpoint networks. Passpoint networks are also considered as "saved" 104 * network, but without being persisted to the storage. They are managed 105 * by {@link PasspointNetworkEvaluator}. 106 */ 107 if (network.isPasspoint()) { 108 continue; 109 } 110 111 WifiConfiguration.NetworkSelectionStatus status = 112 network.getNetworkSelectionStatus(); 113 114 // If a configuration is temporarily disabled, re-enable it before trying 115 // to connect to it. 116 mWifiConfigManager.tryEnableNetwork(network.networkId); 117 118 //TODO(b/30928589): Enable "permanently" disabled networks if we are in DISCONNECTED 119 // state. 120 121 // Clear the cached candidate, score and seen. 122 mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); 123 124 boolean networkDisabled = false; 125 boolean networkStringLogged = false; 126 for (int index = WifiConfiguration.NetworkSelectionStatus 127 .NETWORK_SELECTION_DISABLED_STARTING_INDEX; 128 index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; 129 index++) { 130 int count = status.getDisableReasonCounter(index); 131 if (count > 0) { 132 networkDisabled = true; 133 if (!networkStringLogged) { 134 sbuf.append(" ").append(WifiNetworkSelector.toNetworkString(network)) 135 .append(" "); 136 networkStringLogged = true; 137 } 138 sbuf.append("reason=") 139 .append(WifiConfiguration.NetworkSelectionStatus 140 .getNetworkDisableReasonString(index)) 141 .append(", count=").append(count).append("; "); 142 } 143 } 144 145 if (networkDisabled) { 146 sbuf.append("\n"); 147 } 148 } 149 150 if (sbuf.length() > 0) { 151 localLog("Disabled saved networks:"); 152 localLog(sbuf.toString()); 153 } 154 } 155 156 /** 157 * Update the evaluator. 158 */ 159 public void update(List<ScanDetail> scanDetails) { 160 updateSavedNetworkSelectionStatus(); 161 } 162 163 private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network, 164 WifiConfiguration currentNetwork, String currentBssid, 165 StringBuffer sbuf) { 166 int score = 0; 167 boolean is5GHz = scanResult.is5GHz(); 168 169 sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID) 170 .append(" RSSI:").append(scanResult.level).append(" ] "); 171 // Calculate the RSSI score. 172 int rssiSaturationThreshold = is5GHz ? mThresholdSaturatedRssi5 : mThresholdSaturatedRssi24; 173 int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level 174 : rssiSaturationThreshold; 175 score += (rssi + mRssiScoreOffset) * mRssiScoreSlope; 176 sbuf.append(" RSSI score: ").append(score).append(","); 177 178 // 5GHz band bonus. 179 if (is5GHz) { 180 score += mBand5GHzAward; 181 sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(","); 182 } 183 184 // Last user selection award. 185 int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork(); 186 if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID 187 && lastUserSelectedNetworkId == network.networkId) { 188 long timeDifference = mClock.getElapsedSinceBootMillis() 189 - mWifiConfigManager.getLastSelectedTimeStamp(); 190 if (timeDifference > 0) { 191 int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60); 192 score += bonus > 0 ? bonus : 0; 193 sbuf.append(" User selection ").append(timeDifference / 1000 / 60) 194 .append(" minutes ago, bonus: ").append(bonus).append(","); 195 } 196 } 197 198 // Same network award. 199 if (currentNetwork != null 200 && (network.networkId == currentNetwork.networkId 201 //TODO(b/36788683): re-enable linked configuration check 202 /* || network.isLinked(currentNetwork) */)) { 203 score += mSameNetworkAward; 204 sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(","); 205 206 // When firmware roaming is supported, equivalent BSSIDs (the ones under the 207 // same network as the currently connected one) get the same BSSID award. 208 if (mConnectivityHelper.isFirmwareRoamingSupported() 209 && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) { 210 score += mSameBssidAward; 211 sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(","); 212 } 213 } 214 215 // Same BSSID award. 216 if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) { 217 score += mSameBssidAward; 218 sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(","); 219 } 220 221 // Security award. 222 if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) { 223 score += mSecurityAward; 224 sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(","); 225 } 226 227 sbuf.append(" ## Total score: ").append(score).append("\n"); 228 229 return score; 230 } 231 232 /** 233 * Evaluate all the networks from the scan results and return 234 * the WifiConfiguration of the network chosen for connection. 235 * 236 * @return configuration of the chosen network; 237 * null if no network in this category is available. 238 */ 239 public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails, 240 WifiConfiguration currentNetwork, String currentBssid, boolean connected, 241 boolean untrustedNetworkAllowed, 242 List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) { 243 int highestScore = Integer.MIN_VALUE; 244 ScanResult scanResultCandidate = null; 245 WifiConfiguration candidate = null; 246 StringBuffer scoreHistory = new StringBuffer(); 247 248 for (ScanDetail scanDetail : scanDetails) { 249 ScanResult scanResult = scanDetail.getScanResult(); 250 int highestScoreOfScanResult = Integer.MIN_VALUE; 251 int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID; 252 253 // One ScanResult can be associated with more than one networks, hence we calculate all 254 // the scores and use the highest one as the ScanResult's score. 255 List<WifiConfiguration> associatedConfigurations = null; 256 WifiConfiguration associatedConfiguration = 257 mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail); 258 259 if (associatedConfiguration == null) { 260 continue; 261 } else { 262 associatedConfigurations = 263 new ArrayList<>(Arrays.asList(associatedConfiguration)); 264 } 265 266 for (WifiConfiguration network : associatedConfigurations) { 267 /** 268 * Ignore Passpoint networks. Passpoint networks are also considered as "saved" 269 * network, but without being persisted to the storage. They are being evaluated 270 * by {@link PasspointNetworkEvaluator}. 271 */ 272 if (network.isPasspoint()) { 273 continue; 274 } 275 276 WifiConfiguration.NetworkSelectionStatus status = 277 network.getNetworkSelectionStatus(); 278 status.setSeenInLastQualifiedNetworkSelection(true); 279 280 if (!status.isNetworkEnabled()) { 281 continue; 282 } else if (network.BSSID != null && !network.BSSID.equals("any") 283 && !network.BSSID.equals(scanResult.BSSID)) { 284 // App has specified the only BSSID to connect for this 285 // configuration. So only the matching ScanResult can be a candidate. 286 localLog("Network " + WifiNetworkSelector.toNetworkString(network) 287 + " has specified BSSID " + network.BSSID + ". Skip " 288 + scanResult.BSSID); 289 continue; 290 } else if (TelephonyUtil.isSimConfig(network) 291 && !mWifiConfigManager.isSimPresent()) { 292 // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present. 293 continue; 294 } 295 296 int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid, 297 scoreHistory); 298 299 // Set candidate ScanResult for all saved networks to ensure that users can 300 // override network selection. See WifiNetworkSelector#setUserConnectChoice. 301 // TODO(b/36067705): consider alternative designs to push filtering/selecting of 302 // user connect choice networks to RecommendedNetworkEvaluator. 303 if (score > status.getCandidateScore() || (score == status.getCandidateScore() 304 && status.getCandidate() != null 305 && scanResult.level > status.getCandidate().level)) { 306 mWifiConfigManager.setNetworkCandidateScanResult( 307 network.networkId, scanResult, score); 308 } 309 310 // If the network is marked to use external scores, or is an open network with 311 // curate saved open networks enabled, do not consider it for network selection. 312 if (network.useExternalScores) { 313 localLog("Network " + WifiNetworkSelector.toNetworkString(network) 314 + " has external score."); 315 continue; 316 } 317 318 if (score > highestScoreOfScanResult) { 319 highestScoreOfScanResult = score; 320 candidateIdOfScanResult = network.networkId; 321 } 322 } 323 324 if (connectableNetworks != null) { 325 connectableNetworks.add(Pair.create(scanDetail, 326 mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult))); 327 } 328 329 if (highestScoreOfScanResult > highestScore 330 || (highestScoreOfScanResult == highestScore 331 && scanResultCandidate != null 332 && scanResult.level > scanResultCandidate.level)) { 333 highestScore = highestScoreOfScanResult; 334 scanResultCandidate = scanResult; 335 mWifiConfigManager.setNetworkCandidateScanResult( 336 candidateIdOfScanResult, scanResultCandidate, highestScore); 337 // Reload the network config with the updated info. 338 candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult); 339 } 340 } 341 342 if (scoreHistory.length() > 0) { 343 localLog("\n" + scoreHistory.toString()); 344 } 345 346 if (scanResultCandidate == null) { 347 localLog("did not see any good candidates."); 348 } 349 return candidate; 350 } 351} 352