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