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 static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; 20 import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED; 21 import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED; 22 import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE; 23 24 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; 25 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.SuppressLint; 30 import android.app.admin.DevicePolicyManager; 31 import android.app.admin.WifiSsidPolicy; 32 import android.content.Context; 33 import android.net.MacAddress; 34 import android.net.wifi.ScanResult; 35 import android.net.wifi.SecurityParams; 36 import android.net.wifi.SupplicantState; 37 import android.net.wifi.WifiAnnotations; 38 import android.net.wifi.WifiConfiguration; 39 import android.net.wifi.WifiInfo; 40 import android.net.wifi.WifiNetworkSelectionConfig.AssociatedNetworkSelectionOverride; 41 import android.net.wifi.WifiSsid; 42 import android.net.wifi.util.ScanResultUtil; 43 import android.telephony.TelephonyManager; 44 import android.text.TextUtils; 45 import android.util.ArrayMap; 46 import android.util.ArraySet; 47 import android.util.LocalLog; 48 import android.util.Log; 49 import android.util.Pair; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.util.Preconditions; 53 import com.android.modules.utils.build.SdkLevel; 54 import com.android.server.wifi.hotspot2.NetworkDetail; 55 import com.android.server.wifi.proto.nano.WifiMetricsProto; 56 import com.android.server.wifi.util.InformationElementUtil.BssLoad; 57 import com.android.server.wifi.util.WifiPermissionsUtil; 58 import com.android.wifi.resources.R; 59 60 import java.lang.annotation.Retention; 61 import java.lang.annotation.RetentionPolicy; 62 import java.util.ArrayList; 63 import java.util.Collection; 64 import java.util.Comparator; 65 import java.util.HashSet; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Objects; 69 import java.util.Set; 70 import java.util.StringJoiner; 71 import java.util.concurrent.TimeUnit; 72 import java.util.stream.Collectors; 73 74 /** 75 * WifiNetworkSelector looks at all the connectivity scan results and 76 * runs all the nominators to find or create matching configurations. 77 * Then it makes a final selection from among the resulting candidates. 78 */ 79 public class WifiNetworkSelector { 80 private static final String TAG = "WifiNetworkSelector"; 81 82 private static final long INVALID_TIME_STAMP = Long.MIN_VALUE; 83 84 /** 85 * Minimum time gap between last successful network selection and a 86 * new selection attempt. 87 */ 88 @VisibleForTesting 89 public static final int MINIMUM_NETWORK_SELECTION_INTERVAL_MS = 10 * 1000; 90 91 /** 92 * Connected score value used to decide whether a still-connected wifi should be treated 93 * as unconnected when filtering scan results. 94 */ 95 @VisibleForTesting 96 public static final int WIFI_POOR_SCORE = ConnectedScore.WIFI_TRANSITION_SCORE - 10; 97 98 /** 99 * The identifier string of the CandidateScorer to use (in the absence of overrides). 100 */ 101 public static final String PRESET_CANDIDATE_SCORER_NAME = "ThroughputScorer"; 102 103 /** 104 * Experiment ID for the legacy scorer. 105 */ 106 public static final int LEGACY_CANDIDATE_SCORER_EXP_ID = 0; 107 108 private final Context mContext; 109 private final WifiConfigManager mWifiConfigManager; 110 private final Clock mClock; 111 private final LocalLog mLocalLog; 112 private boolean mVerboseLoggingEnabled = false; 113 private final WifiMetrics mWifiMetrics; 114 private long mLastNetworkSelectionTimeStamp = INVALID_TIME_STAMP; 115 // Buffer of filtered scan results (Scan results considered by network selection) & associated 116 // WifiConfiguration (if any). 117 private final List<Pair<ScanDetail, WifiConfiguration>> mConnectableNetworks = 118 new ArrayList<>(); 119 private List<ScanDetail> mFilteredNetworks = new ArrayList<>(); 120 private final WifiScoreCard mWifiScoreCard; 121 private final ScoringParams mScoringParams; 122 private final WifiInjector mWifiInjector; 123 private final ThroughputPredictor mThroughputPredictor; 124 private final WifiChannelUtilization mWifiChannelUtilization; 125 private final WifiGlobals mWifiGlobals; 126 private final ScanRequestProxy mScanRequestProxy; 127 128 private final Map<String, WifiCandidates.CandidateScorer> mCandidateScorers = new ArrayMap<>(); 129 private boolean mIsEnhancedOpenSupportedInitialized = false; 130 private boolean mIsEnhancedOpenSupported; 131 private boolean mSufficiencyCheckEnabledWhenScreenOff = true; 132 private boolean mSufficiencyCheckEnabledWhenScreenOn = true; 133 private boolean mUserConnectChoiceOverrideEnabled = true; 134 private boolean mLastSelectionWeightEnabled = true; 135 private @AssociatedNetworkSelectionOverride int mAssociatedNetworkSelectionOverride = 136 ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE; 137 private boolean mScreenOn = false; 138 private final WifiNative mWifiNative; 139 private final DevicePolicyManager mDevicePolicyManager; 140 141 /** 142 * Interface for WiFi Network Nominator 143 * 144 * A network nominator examines the scan results reports the 145 * connectable candidates in its category for further consideration. 146 */ 147 public interface NetworkNominator { 148 /** Type of nominators */ 149 int NOMINATOR_ID_SAVED = 0; 150 int NOMINATOR_ID_SUGGESTION = 1; 151 int NOMINATOR_ID_SCORED = 4; 152 int NOMINATOR_ID_CURRENT = 5; // Should always be last 153 154 @IntDef(prefix = {"NOMINATOR_ID_"}, value = { 155 NOMINATOR_ID_SAVED, 156 NOMINATOR_ID_SUGGESTION, 157 NOMINATOR_ID_SCORED, 158 NOMINATOR_ID_CURRENT}) 159 @Retention(RetentionPolicy.SOURCE) 160 public @interface NominatorId { 161 } 162 163 /** 164 * Get the nominator type. 165 */ 166 @NominatorId getId()167 int getId(); 168 169 /** 170 * Get the nominator name. 171 */ getName()172 String getName(); 173 174 /** 175 * Update the nominator. 176 * 177 * Certain nominators have to be updated with the new scan results. For example 178 * the ScoredNetworkNominator needs to refresh its Score Cache. 179 * 180 * @param scanDetails a list of scan details constructed from the scan results 181 */ update(List<ScanDetail> scanDetails)182 void update(List<ScanDetail> scanDetails); 183 184 /** 185 * Evaluate all the networks from the scan results. 186 * 187 * @param scanDetails a list of scan details constructed from the scan 188 * results 189 * @param untrustedNetworkAllowed a flag to indicate if untrusted networks are allowed 190 * @param oemPaidNetworkAllowed a flag to indicate if oem paid networks are allowed 191 * @param oemPrivateNetworkAllowed a flag to indicate if oem private networks are 192 * allowed 193 * @param restrictedNetworkAllowedUids a set of Uids are allowed for restricted network 194 * @param onConnectableListener callback to record all of the connectable networks 195 */ nominateNetworks(@onNull List<ScanDetail> scanDetails, @NonNull List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, @NonNull Set<Integer> restrictedNetworkAllowedUids, @NonNull OnConnectableListener onConnectableListener)196 void nominateNetworks(@NonNull List<ScanDetail> scanDetails, 197 @NonNull List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates, 198 boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, 199 boolean oemPrivateNetworkAllowed, 200 @NonNull Set<Integer> restrictedNetworkAllowedUids, 201 @NonNull OnConnectableListener onConnectableListener); 202 203 /** 204 * Callback for recording connectable candidates 205 */ 206 public interface OnConnectableListener { 207 /** 208 * Notes that an access point is an eligible connection candidate 209 * 210 * @param scanDetail describes the specific access point 211 * @param config is the WifiConfiguration for the network 212 */ onConnectable(ScanDetail scanDetail, WifiConfiguration config)213 void onConnectable(ScanDetail scanDetail, WifiConfiguration config); 214 } 215 } 216 217 private final List<NetworkNominator> mNominators = new ArrayList<>(3); 218 219 // A helper to log debugging information in the local log buffer, which can 220 // be retrieved in bugreport. It is also used to print the log in the console. localLog(String log)221 private void localLog(String log) { 222 mLocalLog.log(log); 223 if (mVerboseLoggingEnabled) Log.d(TAG, log, null); 224 } 225 226 /** 227 * Enable verbose logging in the console 228 */ enableVerboseLogging(boolean verbose)229 public void enableVerboseLogging(boolean verbose) { 230 mVerboseLoggingEnabled = verbose; 231 } 232 233 /** 234 * Check if current network has sufficient RSSI 235 * 236 * @param wifiInfo info of currently connected network 237 * @return true if current link quality is sufficient, false otherwise. 238 */ hasSufficientLinkQuality(WifiInfo wifiInfo)239 public boolean hasSufficientLinkQuality(WifiInfo wifiInfo) { 240 int currentRssi = wifiInfo.getRssi(); 241 return currentRssi >= mScoringParams.getSufficientRssi(wifiInfo.getFrequency()); 242 } 243 244 /** 245 * Check if current network has active Tx or Rx traffic 246 * 247 * @param wifiInfo info of currently connected network 248 * @return true if it has active Tx or Rx traffic, false otherwise. 249 */ hasActiveStream(WifiInfo wifiInfo)250 public boolean hasActiveStream(WifiInfo wifiInfo) { 251 return wifiInfo.getSuccessfulTxPacketsPerSecond() 252 > mScoringParams.getActiveTrafficPacketsPerSecond() 253 || wifiInfo.getSuccessfulRxPacketsPerSecond() 254 > mScoringParams.getActiveTrafficPacketsPerSecond(); 255 } 256 257 /** 258 * Check if current network has internet or is expected to not have internet 259 */ hasInternetOrExpectNoInternet(WifiInfo wifiInfo)260 public boolean hasInternetOrExpectNoInternet(WifiInfo wifiInfo) { 261 WifiConfiguration network = 262 mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId()); 263 if (network == null) { 264 return false; 265 } 266 return !network.hasNoInternetAccess() || network.isNoInternetAccessExpected(); 267 } 268 /** 269 * Determines whether the currently connected network is sufficient. 270 * 271 * If the network is good enough, or if switching to a new network is likely to 272 * be disruptive, we should avoid doing a network selection. 273 * 274 * @param wifiInfo info of currently connected network 275 * @return true if the network is sufficient 276 */ isNetworkSufficient(WifiInfo wifiInfo)277 public boolean isNetworkSufficient(WifiInfo wifiInfo) { 278 // Currently connected? 279 if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) { 280 return false; 281 } 282 283 localLog("Current connected network: " + wifiInfo.getNetworkId()); 284 285 WifiConfiguration network = 286 mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId()); 287 288 if (network == null) { 289 localLog("Current network was removed"); 290 return false; 291 } 292 293 // Skip autojoin for the first few seconds of a user-initiated connection. 294 // This delays network selection during the time that connectivity service may be posting 295 // a dialog about a no-internet network. 296 if (mWifiConfigManager.getLastSelectedNetwork() == network.networkId 297 && (mClock.getElapsedSinceBootMillis() 298 - mWifiConfigManager.getLastSelectedTimeStamp()) 299 <= mContext.getResources().getInteger( 300 R.integer.config_wifiSufficientDurationAfterUserSelectionMilliseconds)) { 301 localLog("Current network is recently user-selected"); 302 return true; 303 } 304 305 // Set OSU (Online Sign Up) network for Passpoint Release 2 to sufficient 306 // so that network select selection is skipped and OSU process can complete. 307 if (network.osu) { 308 localLog("Current connection is OSU"); 309 return true; 310 } 311 312 // Current network is set as unusable by the external scorer. 313 if (!wifiInfo.isUsable()) { 314 localLog("Wifi is unusable according to external scorer."); 315 return false; 316 } 317 318 // External scorer is not being used, and the current network's score is below the 319 // sufficient score threshold configured for the AOSP scorer. 320 if (!mWifiGlobals.isUsingExternalScorer() && wifiInfo.getScore() 321 < mWifiGlobals.getWifiLowConnectedScoreThresholdToTriggerScanForMbb()) { 322 if (!SdkLevel.isAtLeastS()) { 323 // Return false to prevent build issues since WifiInfo#isPrimary is only supported 324 // on S and above. 325 return false; 326 } 327 // Only return false to trigger network selection on the primary, since the secondary 328 // STA is not scored. 329 if (wifiInfo.isPrimary()) { 330 return false; 331 } 332 } 333 334 // OEM paid/private networks are only available to system apps, so this is never sufficient. 335 if (network.oemPaid || network.oemPrivate) { 336 localLog("Current network is oem paid/private"); 337 return false; 338 } 339 340 // Metered networks costs the user data, so this is insufficient. 341 if (WifiConfiguration.isMetered(network, wifiInfo)) { 342 localLog("Current network is metered"); 343 return false; 344 } 345 346 // Network without internet access is not sufficient, unless expected 347 if (!hasInternetOrExpectNoInternet(wifiInfo)) { 348 localLog("Current network has [" + network.numNoInternetAccessReports 349 + "] no-internet access reports"); 350 return false; 351 } 352 353 if (!isSufficiencyCheckEnabled()) { 354 localLog("Current network assumed as insufficient because sufficiency check is " 355 + "disabled. mScreenOn=" + mScreenOn); 356 return false; 357 } 358 359 if (network.isIpProvisioningTimedOut()) { 360 localLog("Current network has no IPv4 provisioning and therefore insufficient"); 361 return false; 362 } 363 364 if (!hasSufficientLinkQuality(wifiInfo) && !hasActiveStream(wifiInfo)) { 365 localLog("Current network link quality is not sufficient and has low ongoing traffic"); 366 return false; 367 } 368 369 return true; 370 } 371 372 /** 373 * Get whether associated network selection is enabled. 374 * @return 375 */ isAssociatedNetworkSelectionEnabled()376 public boolean isAssociatedNetworkSelectionEnabled() { 377 if (mAssociatedNetworkSelectionOverride == ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE) { 378 return mContext.getResources().getBoolean( 379 R.bool.config_wifi_framework_enable_associated_network_selection); 380 } 381 return mAssociatedNetworkSelectionOverride == ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED; 382 } 383 384 /** 385 * Check if network selection is needed on a CMM. 386 * @return True if network selection is needed. False if not needed. 387 */ isNetworkSelectionNeededForCmm(@onNull ClientModeManagerState cmmState)388 public boolean isNetworkSelectionNeededForCmm(@NonNull ClientModeManagerState cmmState) { 389 if (cmmState.connected) { 390 // Is roaming allowed? 391 if (!isAssociatedNetworkSelectionEnabled()) { 392 localLog(cmmState.ifaceName + ": Switching networks in connected state is not " 393 + "allowed. Skip network selection."); 394 return false; 395 } 396 397 // Has it been at least the minimum interval since last network selection? 398 if (mLastNetworkSelectionTimeStamp != INVALID_TIME_STAMP) { 399 long gap = mClock.getElapsedSinceBootMillis() 400 - mLastNetworkSelectionTimeStamp; 401 if (gap < MINIMUM_NETWORK_SELECTION_INTERVAL_MS) { 402 localLog(cmmState.ifaceName + ": Too short since last network selection: " 403 + gap + " ms. Skip network selection."); 404 return false; 405 } 406 } 407 // Please note other scans (e.g., location scan or app scan) may also trigger network 408 // selection and these scans may or may not run sufficiency check. 409 // So it is better to run sufficiency check here before network selection. 410 if (isNetworkSufficient(cmmState.wifiInfo)) { 411 localLog(cmmState.ifaceName 412 + ": Current connected network already sufficient." 413 + " Skip network selection."); 414 return false; 415 } else { 416 localLog(cmmState.ifaceName + ": Current connected network is not sufficient."); 417 return true; 418 } 419 } else if (cmmState.disconnected || cmmState.ipProvisioningTimedOut) { 420 return true; 421 } else { 422 // No network selection if ClientModeImpl is in a state other than 423 // connected or disconnected (i.e connecting). 424 localLog(cmmState.ifaceName + ": ClientModeImpl is in neither CONNECTED nor " 425 + "DISCONNECTED state. Skip network selection."); 426 return false; 427 } 428 429 } 430 isNetworkSelectionNeeded(@onNull List<ClientModeManagerState> cmmStates)431 private boolean isNetworkSelectionNeeded(@NonNull List<ClientModeManagerState> cmmStates) { 432 for (ClientModeManagerState cmmState : cmmStates) { 433 // network selection needed by this CMM instance, perform network selection 434 if (isNetworkSelectionNeededForCmm(cmmState)) { 435 return true; 436 } 437 } 438 // none of the CMM instances need network selection, skip network selection. 439 return false; 440 } 441 442 /** 443 * Format the given ScanResult as a scan ID for logging. 444 */ toScanId(@ullable ScanResult scanResult)445 public static String toScanId(@Nullable ScanResult scanResult) { 446 return scanResult == null ? "NULL" 447 : scanResult.SSID + ":" + scanResult.BSSID; 448 } 449 450 /** 451 * Format the given WifiConfiguration as a SSID:netId string 452 */ toNetworkString(WifiConfiguration network)453 public static String toNetworkString(WifiConfiguration network) { 454 if (network == null) { 455 return null; 456 } 457 458 return (network.SSID + ":" + network.networkId); 459 } 460 461 /** 462 * Compares ScanResult level against the minimum threshold for its band, returns true if lower 463 */ isSignalTooWeak(ScanResult scanResult)464 public boolean isSignalTooWeak(ScanResult scanResult) { 465 return (scanResult.level < mScoringParams.getEntryRssi(scanResult.frequency)); 466 } 467 468 @SuppressLint("NewApi") filterScanResults(List<ScanDetail> scanDetails, Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates)469 private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails, 470 Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates) { 471 List<ScanDetail> validScanDetails = new ArrayList<>(); 472 StringBuffer noValidSsid = new StringBuffer(); 473 StringBuffer blockedBssid = new StringBuffer(); 474 StringBuffer lowRssi = new StringBuffer(); 475 StringBuffer mboAssociationDisallowedBssid = new StringBuffer(); 476 StringBuffer adminRestrictedSsid = new StringBuffer(); 477 StringJoiner deprecatedSecurityTypeSsid = new StringJoiner(" / "); 478 List<String> currentBssids = cmmStates.stream() 479 .map(cmmState -> cmmState.wifiInfo.getBSSID()) 480 .collect(Collectors.toList()); 481 Set<String> scanResultPresentForCurrentBssids = new ArraySet<>(); 482 483 int adminMinimumSecurityLevel = 0; 484 boolean adminSsidRestrictionSet = false; 485 Set<WifiSsid> adminSsidAllowlist = new ArraySet<>(); 486 Set<WifiSsid> admindSsidDenylist = new ArraySet<>(); 487 488 int numBssidFiltered = 0; 489 490 if (mDevicePolicyManager != null && SdkLevel.isAtLeastT()) { 491 adminMinimumSecurityLevel = 492 mDevicePolicyManager.getMinimumRequiredWifiSecurityLevel(); 493 WifiSsidPolicy policy = mDevicePolicyManager.getWifiSsidPolicy(); 494 if (policy != null) { 495 adminSsidRestrictionSet = true; 496 if (policy.getPolicyType() == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) { 497 adminSsidAllowlist = policy.getSsids(); 498 } else { 499 admindSsidDenylist = policy.getSsids(); 500 } 501 } 502 } 503 504 for (ScanDetail scanDetail : scanDetails) { 505 ScanResult scanResult = scanDetail.getScanResult(); 506 507 if (TextUtils.isEmpty(scanResult.SSID)) { 508 noValidSsid.append(scanResult.BSSID).append(" / "); 509 continue; 510 } 511 512 // Check if the scan results contain the currently connected BSSID's 513 if (currentBssids.contains(scanResult.BSSID)) { 514 scanResultPresentForCurrentBssids.add(scanResult.BSSID); 515 validScanDetails.add(scanDetail); 516 continue; 517 } 518 519 final String scanId = toScanId(scanResult); 520 521 if (bssidBlocklist.contains(scanResult.BSSID)) { 522 blockedBssid.append(scanId).append(" / "); 523 numBssidFiltered++; 524 continue; 525 } 526 527 // Skip network with too weak signals. 528 if (isSignalTooWeak(scanResult)) { 529 lowRssi.append(scanId); 530 if (scanResult.is24GHz()) { 531 lowRssi.append("(2.4GHz)"); 532 } else if (scanResult.is5GHz()) { 533 lowRssi.append("(5GHz)"); 534 } else if (scanResult.is6GHz()) { 535 lowRssi.append("(6GHz)"); 536 } 537 lowRssi.append(scanResult.level).append(" / "); 538 continue; 539 } 540 541 // Skip BSS which is not accepting new connections. 542 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 543 if (networkDetail != null) { 544 if (networkDetail.getMboAssociationDisallowedReasonCode() 545 != MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT) { 546 mWifiMetrics 547 .incrementNetworkSelectionFilteredBssidCountDueToMboAssocDisallowInd(); 548 mboAssociationDisallowedBssid.append(scanId).append("(") 549 .append(networkDetail.getMboAssociationDisallowedReasonCode()) 550 .append(")").append(" / "); 551 continue; 552 } 553 } 554 555 // Skip network that does not meet the admin set SSID restriction 556 if (adminSsidRestrictionSet) { 557 WifiSsid ssid = scanResult.getWifiSsid(); 558 // Allowlist policy set but network is not present in the list 559 if (!adminSsidAllowlist.isEmpty() && !adminSsidAllowlist.contains(ssid)) { 560 adminRestrictedSsid.append(scanId).append(" / "); 561 continue; 562 } 563 // Denylist policy set but network is present in the list 564 if (!admindSsidDenylist.isEmpty() && admindSsidDenylist.contains(ssid)) { 565 adminRestrictedSsid.append(scanId).append(" / "); 566 continue; 567 } 568 } 569 570 // Skip network that does not meet the admin set minimum security level restriction 571 if (adminMinimumSecurityLevel != 0) { 572 boolean securityRestrictionPassed = false; 573 @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); 574 for (int type : securityTypes) { 575 int securityLevel = WifiInfo.convertSecurityTypeToDpmWifiSecurity(type); 576 577 // Skip unknown security type since security level cannot be determined. 578 // If all the security types are unknown when the minimum security level 579 // restriction is set, the scan result is ignored. 580 if (securityLevel == WifiInfo.DPM_SECURITY_TYPE_UNKNOWN) continue; 581 582 if (adminMinimumSecurityLevel <= securityLevel) { 583 securityRestrictionPassed = true; 584 break; 585 } 586 } 587 if (!securityRestrictionPassed) { 588 adminRestrictedSsid.append(scanId).append(" / "); 589 continue; 590 } 591 } 592 593 // Skip network that has deprecated security type 594 if (mWifiGlobals.isWpaPersonalDeprecated() || mWifiGlobals.isWepDeprecated()) { 595 boolean securityTypeDeprecated = false; 596 @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); 597 for (int type : securityTypes) { 598 if (mWifiGlobals.isWepDeprecated() && type == WifiInfo.SECURITY_TYPE_WEP) { 599 securityTypeDeprecated = true; 600 break; 601 } 602 if (mWifiGlobals.isWpaPersonalDeprecated() && type == WifiInfo.SECURITY_TYPE_PSK 603 && ScanResultUtil.isScanResultForWpaPersonalOnlyNetwork(scanResult)) { 604 securityTypeDeprecated = true; 605 break; 606 } 607 } 608 if (securityTypeDeprecated) { 609 deprecatedSecurityTypeSsid.add(scanId); 610 continue; 611 } 612 } 613 614 validScanDetails.add(scanDetail); 615 } 616 mWifiMetrics.incrementNetworkSelectionFilteredBssidCount(numBssidFiltered); 617 618 // WNS listens to all single scan results. Some scan requests may not include 619 // the channel of the currently connected network, so the currently connected 620 // network won't show up in the scan results. We don't act on these scan results 621 // to avoid aggressive network switching which might trigger disconnection. 622 // TODO(b/147751334) this may no longer be needed 623 for (ClientModeManagerState cmmState : cmmStates) { 624 // TODO (b/169413079): Disable network selection on corresponding CMM instead. 625 if (cmmState.connected && cmmState.wifiInfo.getScore() >= WIFI_POOR_SCORE 626 && !scanResultPresentForCurrentBssids.contains(cmmState.wifiInfo.getBSSID())) { 627 if (isSufficiencyCheckEnabled()) { 628 localLog("Current connected BSSID " + cmmState.wifiInfo.getBSSID() 629 + " is not in the scan results. Skip network selection."); 630 validScanDetails.clear(); 631 return validScanDetails; 632 } else { 633 localLog("Current connected BSSID " + cmmState.wifiInfo.getBSSID() 634 + " is not in the scan results. But continue network selection because" 635 + " sufficiency check is disabled."); 636 } 637 } 638 } 639 640 if (noValidSsid.length() != 0) { 641 localLog("Networks filtered out due to invalid SSID: " + noValidSsid); 642 } 643 644 if (blockedBssid.length() != 0) { 645 localLog("Networks filtered out due to blocklist: " + blockedBssid); 646 } 647 648 if (lowRssi.length() != 0) { 649 localLog("Networks filtered out due to low signal strength: " + lowRssi); 650 } 651 652 if (mboAssociationDisallowedBssid.length() != 0) { 653 localLog("Networks filtered out due to mbo association disallowed indication: " 654 + mboAssociationDisallowedBssid); 655 } 656 657 if (adminRestrictedSsid.length() != 0) { 658 localLog("Networks filtered out due to admin restrictions: " + adminRestrictedSsid); 659 } 660 661 if (deprecatedSecurityTypeSsid.length() != 0) { 662 localLog("Networks filtered out due to deprecated security type: " 663 + deprecatedSecurityTypeSsid); 664 } 665 666 return validScanDetails; 667 } 668 findScanDetailForBssid(List<ScanDetail> scanDetails, String currentBssid)669 private ScanDetail findScanDetailForBssid(List<ScanDetail> scanDetails, 670 String currentBssid) { 671 for (ScanDetail scanDetail : scanDetails) { 672 ScanResult scanResult = scanDetail.getScanResult(); 673 if (scanResult.BSSID.equals(currentBssid)) { 674 return scanDetail; 675 } 676 } 677 return null; 678 } 679 isEnhancedOpenSupported()680 private boolean isEnhancedOpenSupported() { 681 if (mIsEnhancedOpenSupportedInitialized) { 682 return mIsEnhancedOpenSupported; 683 } 684 685 mIsEnhancedOpenSupportedInitialized = true; 686 ClientModeManager primaryManager = 687 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 688 mIsEnhancedOpenSupported = (primaryManager.getSupportedFeatures() & WIFI_FEATURE_OWE) != 0; 689 return mIsEnhancedOpenSupported; 690 } 691 692 /** 693 * This returns a list of ScanDetails that were filtered in the process of network selection. 694 * The list is further filtered for only open unsaved networks. 695 * 696 * @return the list of ScanDetails for open unsaved networks that do not have invalid SSIDS, 697 * blocked BSSIDS, or low signal strength. This will return an empty list when there are 698 * no open unsaved networks, or when network selection has not been run. 699 */ getFilteredScanDetailsForOpenUnsavedNetworks()700 public List<ScanDetail> getFilteredScanDetailsForOpenUnsavedNetworks() { 701 List<ScanDetail> openUnsavedNetworks = new ArrayList<>(); 702 boolean enhancedOpenSupported = isEnhancedOpenSupported(); 703 for (ScanDetail scanDetail : mFilteredNetworks) { 704 ScanResult scanResult = scanDetail.getScanResult(); 705 706 if (!ScanResultUtil.isScanResultForOpenNetwork(scanResult)) { 707 continue; 708 } 709 710 // Filter out Enhanced Open networks on devices that do not support it 711 if (ScanResultUtil.isScanResultForOweNetwork(scanResult) 712 && !enhancedOpenSupported) { 713 continue; 714 } 715 716 // Skip saved networks 717 if (mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail) != null) { 718 continue; 719 } 720 721 openUnsavedNetworks.add(scanDetail); 722 } 723 return openUnsavedNetworks; 724 } 725 726 /** 727 * @return the list of ScanDetails scored as potential candidates by the last run of 728 * selectNetwork, this will be empty if Network selector determined no selection was 729 * needed on last run. This includes scan details of sufficient signal strength, and 730 * had an associated WifiConfiguration. 731 */ getConnectableScanDetails()732 public List<Pair<ScanDetail, WifiConfiguration>> getConnectableScanDetails() { 733 return mConnectableNetworks; 734 } 735 736 /** 737 * Iterate thru the list of configured networks (includes all saved network configurations + 738 * any ephemeral network configurations created for passpoint networks, suggestions, carrier 739 * networks, etc) and do the following: 740 * a) Try to re-enable any temporarily enabled networks (if the blocklist duration has expired). 741 * b) Clear the {@link WifiConfiguration.NetworkSelectionStatus#getCandidate()} field for all 742 * of them to identify networks that are present in the current scan result. 743 * c) Log any disabled networks. 744 */ updateConfiguredNetworks()745 private void updateConfiguredNetworks() { 746 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 747 if (configuredNetworks.size() == 0) { 748 localLog("No configured networks."); 749 return; 750 } 751 752 StringBuffer sbuf = new StringBuffer(); 753 for (WifiConfiguration network : configuredNetworks) { 754 // If a configuration is temporarily disabled, re-enable it before trying 755 // to connect to it. 756 mWifiConfigManager.tryEnableNetwork(network.networkId); 757 // Clear the cached candidate, score and seen. 758 mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); 759 760 // Log disabled network. 761 WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); 762 if (!status.isNetworkEnabled()) { 763 sbuf.append(" ").append(toNetworkString(network)).append(" "); 764 for (int index = WifiConfiguration.NetworkSelectionStatus 765 .NETWORK_SELECTION_DISABLED_STARTING_INDEX; 766 index < WifiConfiguration.NetworkSelectionStatus 767 .NETWORK_SELECTION_DISABLED_MAX; 768 index++) { 769 int count = status.getDisableReasonCounter(index); 770 // Here we log the reason as long as its count is greater than zero. The 771 // network may not be disabled because of this particular reason. Logging 772 // this information anyway to help understand what happened to the network. 773 if (count > 0) { 774 sbuf.append("reason=") 775 .append(WifiConfiguration.NetworkSelectionStatus 776 .getNetworkSelectionDisableReasonString(index)) 777 .append(", count=").append(count).append("; "); 778 } 779 } 780 sbuf.append("\n"); 781 } 782 } 783 784 if (sbuf.length() > 0) { 785 localLog("Disabled configured networks:"); 786 localLog(sbuf.toString()); 787 } 788 } 789 790 /** 791 * Overrides the {@code candidate} chosen by the {@link #mNominators} with the user chosen 792 * {@link WifiConfiguration} if one exists. 793 * 794 * @return the user chosen {@link WifiConfiguration} if one exists, {@code candidate} otherwise 795 */ overrideCandidateWithUserConnectChoice( @onNull WifiConfiguration candidate)796 private WifiConfiguration overrideCandidateWithUserConnectChoice( 797 @NonNull WifiConfiguration candidate) { 798 WifiConfiguration tempConfig = Preconditions.checkNotNull(candidate); 799 WifiConfiguration originalCandidate = candidate; 800 ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); 801 802 Set<String> seenNetworks = new HashSet<>(); 803 seenNetworks.add(candidate.getProfileKey()); 804 805 while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) { 806 String key = tempConfig.getNetworkSelectionStatus().getConnectChoice(); 807 int userSelectedRssi = tempConfig.getNetworkSelectionStatus().getConnectChoiceRssi(); 808 tempConfig = mWifiConfigManager.getConfiguredNetwork(key); 809 810 if (tempConfig != null) { 811 if (seenNetworks.contains(tempConfig.getProfileKey())) { 812 Log.wtf(TAG, "user connected network is a loop, use candidate:" 813 + candidate); 814 if (candidate.getNetworkSelectionStatus().getCandidate() != null) { 815 mWifiConfigManager.setLegacyUserConnectChoice(candidate, 816 candidate.getNetworkSelectionStatus().getCandidate().level); 817 } 818 break; 819 } 820 seenNetworks.add(tempConfig.getProfileKey()); 821 WifiConfiguration.NetworkSelectionStatus tempStatus = 822 tempConfig.getNetworkSelectionStatus(); 823 boolean noInternetButInternetIsExpected = !tempConfig.isNoInternetAccessExpected() 824 && tempConfig.hasNoInternetAccess(); 825 if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled() 826 && !noInternetButInternetIsExpected 827 && isUserChoiceRssiCloseToOrGreaterThanExpectedValue( 828 tempStatus.getCandidate().level, userSelectedRssi)) { 829 scanResultCandidate = tempStatus.getCandidate(); 830 candidate = tempConfig; 831 } 832 } else { 833 localLog("Connect choice: " + key + " has no corresponding saved config."); 834 break; 835 } 836 } 837 838 if (candidate != originalCandidate) { 839 localLog("After user selection adjustment, the final candidate is:" 840 + WifiNetworkSelector.toNetworkString(candidate) + " : " 841 + scanResultCandidate.BSSID); 842 mWifiMetrics.setNominatorForNetwork(candidate.networkId, 843 WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED_USER_CONNECT_CHOICE); 844 } 845 return candidate; 846 } 847 isUserChoiceRssiCloseToOrGreaterThanExpectedValue(int observedRssi, int expectedRssi)848 private boolean isUserChoiceRssiCloseToOrGreaterThanExpectedValue(int observedRssi, 849 int expectedRssi) { 850 // The expectedRssi may be 0 for newly upgraded devices which do not have this information, 851 // pass the test for those devices to avoid regression. 852 if (expectedRssi == 0) { 853 return true; 854 } 855 return observedRssi >= expectedRssi - mScoringParams.getEstimateRssiErrorMargin(); 856 } 857 858 859 /** 860 * Indicates whether we have ever seen the network to be metered since wifi was enabled. 861 * 862 * This is sticky to prevent continuous flip-flopping between networks, when the metered 863 * status is learned after association. 864 */ isEverMetered(@onNull WifiConfiguration config, @Nullable WifiInfo info, @NonNull ScanDetail scanDetail)865 private boolean isEverMetered(@NonNull WifiConfiguration config, @Nullable WifiInfo info, 866 @NonNull ScanDetail scanDetail) { 867 // If info does not match config, don't use it. 868 if (info != null && info.getNetworkId() != config.networkId) info = null; 869 boolean metered = WifiConfiguration.isMetered(config, info); 870 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 871 if (networkDetail != null 872 && networkDetail.getAnt() 873 == NetworkDetail.Ant.ChargeablePublic) { 874 metered = true; 875 } 876 mWifiMetrics.addMeteredStat(config, metered); 877 if (config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE) { 878 // User override is in effect; we should trust it 879 if (mKnownMeteredNetworkIds.remove(config.networkId)) { 880 localLog("KnownMeteredNetworkIds = " + mKnownMeteredNetworkIds); 881 } 882 metered = config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED; 883 } else if (mKnownMeteredNetworkIds.contains(config.networkId)) { 884 // Use the saved information 885 metered = true; 886 } else if (metered) { 887 // Update the saved information 888 mKnownMeteredNetworkIds.add(config.networkId); 889 localLog("KnownMeteredNetworkIds = " + mKnownMeteredNetworkIds); 890 } 891 return metered; 892 } 893 894 /** 895 * Returns the set of known metered network ids (for tests. dumpsys, and metrics). 896 */ getKnownMeteredNetworkIds()897 public Set<Integer> getKnownMeteredNetworkIds() { 898 return new ArraySet<>(mKnownMeteredNetworkIds); 899 } 900 901 private final ArraySet<Integer> mKnownMeteredNetworkIds = new ArraySet<>(); 902 903 904 /** 905 * Cleans up state that should go away when wifi is disabled. 906 */ resetOnDisable()907 public void resetOnDisable() { 908 mWifiConfigManager.clearLastSelectedNetwork(); 909 mKnownMeteredNetworkIds.clear(); 910 } 911 912 /** 913 * Container class for passing the ClientModeManager state for each instance that is managed by 914 * WifiConnectivityManager, i.e all {@link ClientModeManager#getRole()} equals 915 * {@link ActiveModeManager#ROLE_CLIENT_PRIMARY} or 916 * {@link ActiveModeManager#ROLE_CLIENT_SECONDARY_LONG_LIVED}. 917 */ 918 public static class ClientModeManagerState { 919 /** Iface Name corresponding to iface (if known) */ 920 public final String ifaceName; 921 /** True if the device is connected */ 922 public final boolean connected; 923 /** True if the device is disconnected */ 924 public final boolean disconnected; 925 /** True if the device is connected in local-only mode due to ip provisioning timeout**/ 926 public final boolean ipProvisioningTimedOut; 927 /** Currently connected network */ 928 public final WifiInfo wifiInfo; 929 public final ActiveModeManager.ClientRole role; 930 ClientModeManagerState(@onNull ClientModeManager clientModeManager)931 ClientModeManagerState(@NonNull ClientModeManager clientModeManager) { 932 ifaceName = clientModeManager.getInterfaceName(); 933 connected = clientModeManager.isConnected(); 934 disconnected = clientModeManager.isDisconnected(); 935 ipProvisioningTimedOut = clientModeManager.isIpProvisioningTimedOut(); 936 wifiInfo = clientModeManager.getConnectionInfo(); 937 role = clientModeManager.getRole(); 938 } 939 ClientModeManagerState()940 ClientModeManagerState() { 941 ifaceName = "unknown"; 942 connected = false; 943 disconnected = true; 944 wifiInfo = new WifiInfo(); 945 ipProvisioningTimedOut = false; 946 role = null; 947 } 948 949 @VisibleForTesting ClientModeManagerState(@onNull String ifaceName, boolean connected, boolean disconnected, @NonNull WifiInfo wifiInfo, boolean ipProvisioningTimedOut, ActiveModeManager.ClientRole role)950 ClientModeManagerState(@NonNull String ifaceName, boolean connected, boolean disconnected, 951 @NonNull WifiInfo wifiInfo, boolean ipProvisioningTimedOut, 952 ActiveModeManager.ClientRole role) { 953 this.ifaceName = ifaceName; 954 this.connected = connected; 955 this.disconnected = disconnected; 956 this.wifiInfo = wifiInfo; 957 this.ipProvisioningTimedOut = ipProvisioningTimedOut; 958 this.role = role; 959 } 960 961 @Override equals(Object that)962 public boolean equals(Object that) { 963 if (this == that) return true; 964 if (!(that instanceof ClientModeManagerState)) return false; 965 ClientModeManagerState thatCmmState = (ClientModeManagerState) that; 966 return Objects.equals(ifaceName, thatCmmState.ifaceName) 967 && connected == thatCmmState.connected 968 && disconnected == thatCmmState.disconnected 969 && role == thatCmmState.role 970 // Since wifiinfo does not have equals currently. 971 && Objects.equals(wifiInfo.getSSID(), thatCmmState.wifiInfo.getSSID()) 972 && Objects.equals(wifiInfo.getBSSID(), thatCmmState.wifiInfo.getBSSID()); 973 } 974 975 @Override hashCode()976 public int hashCode() { 977 return Objects.hash(ifaceName, connected, disconnected, 978 wifiInfo.getSSID(), wifiInfo.getBSSID(), role); 979 } 980 981 @Override toString()982 public String toString() { 983 return "ClientModeManagerState: " + ifaceName 984 + ", role:" + role 985 + ", connection state: " 986 + (connected ? " connected" : (disconnected ? " disconnected" : "unknown")) 987 + ", WifiInfo: " + wifiInfo; 988 } 989 } 990 991 /** 992 * Sets the screen state. 993 */ setScreenState(boolean screenOn)994 public void setScreenState(boolean screenOn) { 995 mScreenOn = screenOn; 996 } 997 998 /** 999 * Sets the associated network selection override. 1000 */ setAssociatedNetworkSelectionOverride( @ssociatedNetworkSelectionOverride int value)1001 public boolean setAssociatedNetworkSelectionOverride( 1002 @AssociatedNetworkSelectionOverride int value) { 1003 if (value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE 1004 && value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED 1005 && value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED) { 1006 localLog("Error in setting associated network selection override. Invalid value=" 1007 + value); 1008 return false; 1009 } 1010 mAssociatedNetworkSelectionOverride = value; 1011 return true; 1012 } 1013 1014 /** 1015 * Get whether sufficiency check is enabled based on the current screen state. 1016 */ isSufficiencyCheckEnabled()1017 public boolean isSufficiencyCheckEnabled() { 1018 return mScreenOn ? mSufficiencyCheckEnabledWhenScreenOn 1019 : mSufficiencyCheckEnabledWhenScreenOff; 1020 } 1021 1022 /** 1023 * Enable or disable sufficiency check. 1024 */ setSufficiencyCheckEnabled(boolean enabledWhileScreenOff, boolean enabledWhileScreenOn)1025 public void setSufficiencyCheckEnabled(boolean enabledWhileScreenOff, 1026 boolean enabledWhileScreenOn) { 1027 mSufficiencyCheckEnabledWhenScreenOff = enabledWhileScreenOff; 1028 mSufficiencyCheckEnabledWhenScreenOn = enabledWhileScreenOn; 1029 } 1030 1031 /** 1032 * Enable or disable candidate override with user connect choice. 1033 */ setUserConnectChoiceOverrideEnabled(boolean enabled)1034 public void setUserConnectChoiceOverrideEnabled(boolean enabled) { 1035 mUserConnectChoiceOverrideEnabled = enabled; 1036 } 1037 1038 /** 1039 * Enable or disable last selection weight. 1040 */ setLastSelectionWeightEnabled(boolean enabled)1041 public void setLastSelectionWeightEnabled(boolean enabled) { 1042 mLastSelectionWeightEnabled = enabled; 1043 } 1044 getConnectChoiceKey(@onNull List<ClientModeManagerState> cmmStates)1045 private String getConnectChoiceKey(@NonNull List<ClientModeManagerState> cmmStates) { 1046 for (ClientModeManagerState cmmState : cmmStates) { 1047 if (cmmState.role != ROLE_CLIENT_PRIMARY) { 1048 continue; 1049 } 1050 WifiConfiguration currentNetwork = 1051 mWifiConfigManager.getConfiguredNetwork(cmmState.wifiInfo.getNetworkId()); 1052 if (currentNetwork != null) { 1053 return currentNetwork.getNetworkSelectionStatus().getConnectChoice(); 1054 } 1055 } 1056 return null; 1057 } 1058 1059 /** 1060 * Returns the list of Candidates from networks in range. 1061 * 1062 * @param scanDetails List of ScanDetail for all the APs in range 1063 * @param bssidBlocklist Blocked BSSIDs 1064 * @param cmmStates State of all long lived client mode manager instances - 1065 * {@link ActiveModeManager#ROLE_CLIENT_PRIMARY} & 1066 * {@link ActiveModeManager#ROLE_CLIENT_SECONDARY_LONG_LIVED}. 1067 * @param untrustedNetworkAllowed True if untrusted networks are allowed for connection 1068 * @param oemPaidNetworkAllowed True if oem paid networks are allowed for connection 1069 * @param oemPrivateNetworkAllowed True if oem private networks are allowed for connection 1070 * @param restrictedNetworkAllowedUids a set of Uids are allowed for restricted network 1071 * @param skipSufficiencyCheck True to skip network sufficiency check 1072 * @return list of valid Candidate(s) 1073 */ getCandidatesFromScan( @onNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist, @NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck)1074 public List<WifiCandidates.Candidate> getCandidatesFromScan( 1075 @NonNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist, 1076 @NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, 1077 boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, 1078 Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck) { 1079 mFilteredNetworks.clear(); 1080 mConnectableNetworks.clear(); 1081 if (scanDetails.size() == 0) { 1082 localLog("Empty connectivity scan result"); 1083 return null; 1084 } 1085 1086 // Update the scan detail cache at the start, even if we skip network selection 1087 updateScanDetailCache(scanDetails); 1088 1089 // Update the registered network nominators. 1090 for (NetworkNominator registeredNominator : mNominators) { 1091 registeredNominator.update(scanDetails); 1092 } 1093 1094 // Filter out unwanted networks. 1095 mFilteredNetworks = filterScanResults(scanDetails, bssidBlocklist, cmmStates); 1096 if (mFilteredNetworks.size() == 0) { 1097 return null; 1098 } 1099 // Update the matching profiles into WifiConfigManager, help displaying Passpoint networks 1100 // in Wifi Picker 1101 mWifiInjector.getPasspointNetworkNominateHelper().updatePasspointConfig(mFilteredNetworks); 1102 1103 boolean networkSelectionNeeded = skipSufficiencyCheck 1104 || isNetworkSelectionNeeded(cmmStates); 1105 final String userConnectChoiceKey; 1106 if (!networkSelectionNeeded) { 1107 if (!isAssociatedNetworkSelectionEnabled()) { 1108 // Skip network selection based on connect choice because associated network 1109 // selection is disabled. 1110 return null; 1111 } 1112 userConnectChoiceKey = getConnectChoiceKey(cmmStates); 1113 if (userConnectChoiceKey == null) { 1114 return null; 1115 } 1116 // Continue candidate selection but only allow the user connect choice as candidate 1117 localLog("Current network is sufficient. Continue network selection only " 1118 + "considering user connect choice: " + userConnectChoiceKey); 1119 } else { 1120 userConnectChoiceKey = null; 1121 } 1122 1123 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext); 1124 if (userConnectChoiceKey == null) { 1125 // Add connected network as candidates unless only considering connect choice. 1126 for (ClientModeManagerState cmmState : cmmStates) { 1127 // Always get the current BSSID from WifiInfo in case that firmware initiated 1128 // roaming happened. 1129 String currentBssid = cmmState.wifiInfo.getBSSID(); 1130 WifiConfiguration currentNetwork = 1131 mWifiConfigManager.getConfiguredNetwork(cmmState.wifiInfo.getNetworkId()); 1132 if (currentNetwork != null) { 1133 wifiCandidates.setCurrent(currentNetwork.networkId, currentBssid); 1134 // We always want the current network to be a candidate so that it can 1135 // participate. 1136 // It may also get re-added by a nominator, in which case this fallback 1137 // will be replaced. 1138 MacAddress bssid = MacAddress.fromString(currentBssid); 1139 SecurityParams params = currentNetwork.getNetworkSelectionStatus() 1140 .getLastUsedSecurityParams(); 1141 if (null == params) { 1142 localLog("No known candidate security params for current network."); 1143 continue; 1144 } 1145 WifiCandidates.Key key = new WifiCandidates.Key( 1146 ScanResultMatchInfo.fromWifiConfiguration(currentNetwork), 1147 bssid, currentNetwork.networkId, 1148 params.getSecurityType()); 1149 ScanDetail scanDetail = findScanDetailForBssid(mFilteredNetworks, currentBssid); 1150 int predictedTputMbps = (scanDetail == null) ? 0 1151 : predictThroughput(scanDetail); 1152 wifiCandidates.add(key, currentNetwork, 1153 NetworkNominator.NOMINATOR_ID_CURRENT, 1154 cmmState.wifiInfo.getRssi(), 1155 cmmState.wifiInfo.getFrequency(), 1156 ScanResult.CHANNEL_WIDTH_20MHZ, // channel width unavailable in WifiInfo 1157 calculateLastSelectionWeight(currentNetwork.networkId, 1158 WifiConfiguration.isMetered(currentNetwork, cmmState.wifiInfo)), 1159 WifiConfiguration.isMetered(currentNetwork, cmmState.wifiInfo), 1160 isFromCarrierOrPrivilegedApp(currentNetwork), 1161 predictedTputMbps, 1162 (scanDetail != null) ? scanDetail.getScanResult().getApMldMacAddress() 1163 : null); 1164 } 1165 } 1166 } 1167 1168 // Update all configured networks before initiating network selection. 1169 updateConfiguredNetworks(); 1170 1171 List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates = mWifiInjector 1172 .getPasspointNetworkNominateHelper() 1173 .getPasspointNetworkCandidates(new ArrayList<>(mFilteredNetworks)); 1174 for (NetworkNominator registeredNominator : mNominators) { 1175 localLog("About to run " + registeredNominator.getName() + " :"); 1176 registeredNominator.nominateNetworks( 1177 new ArrayList<>(mFilteredNetworks), passpointCandidates, 1178 untrustedNetworkAllowed, oemPaidNetworkAllowed, oemPrivateNetworkAllowed, 1179 restrictedNetworkAllowedUids, (scanDetail, config) -> { 1180 WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig( 1181 scanDetail, config); 1182 if (key != null) { 1183 if (userConnectChoiceKey != null 1184 && !userConnectChoiceKey.equals(config.getProfileKey())) { 1185 return; 1186 } 1187 boolean metered = false; 1188 for (ClientModeManagerState cmmState : cmmStates) { 1189 if (isEverMetered(config, cmmState.wifiInfo, scanDetail)) { 1190 metered = true; 1191 break; 1192 } 1193 } 1194 // TODO(b/151981920) Saved passpoint candidates are marked ephemeral 1195 boolean added = wifiCandidates.add(key, config, 1196 registeredNominator.getId(), 1197 scanDetail.getScanResult().level, 1198 scanDetail.getScanResult().frequency, 1199 scanDetail.getScanResult().channelWidth, 1200 calculateLastSelectionWeight(config.networkId, metered), 1201 metered, 1202 isFromCarrierOrPrivilegedApp(config), 1203 predictThroughput(scanDetail), 1204 scanDetail.getScanResult().getApMldMacAddress()); 1205 if (added) { 1206 mConnectableNetworks.add(Pair.create(scanDetail, config)); 1207 mWifiConfigManager.updateScanDetailForNetwork( 1208 config.networkId, scanDetail); 1209 mWifiMetrics.setNominatorForNetwork(config.networkId, 1210 toProtoNominatorId(registeredNominator.getId())); 1211 } 1212 } 1213 }); 1214 } 1215 if (mConnectableNetworks.size() != wifiCandidates.size()) { 1216 localLog("Connectable: " + mConnectableNetworks.size() 1217 + " Candidates: " + wifiCandidates.size()); 1218 } 1219 1220 // Update multi link candidate throughput before network selection. 1221 updateMultiLinkCandidatesThroughput(wifiCandidates); 1222 1223 return wifiCandidates.getCandidates(); 1224 } 1225 1226 /** 1227 * Check Wi-Fi7 is enabled for all candidates. 1228 */ isWifi7Enabled(List<WifiCandidates.Candidate> candidates)1229 private boolean isWifi7Enabled(List<WifiCandidates.Candidate> candidates) { 1230 for (WifiCandidates.Candidate candidate : candidates) { 1231 if (!mWifiConfigManager.isWifi7Enabled(candidate.getNetworkConfigId())) return false; 1232 } 1233 return true; 1234 } 1235 1236 /** 1237 * Update multi link candidate's throughput which is used in network selection by 1238 * {@link ThroughputScorer} 1239 * 1240 * Algorithm: 1241 * {@link WifiNative#getSupportedBandCombinations(String)} returns a list of band combinations 1242 * supported by the chip. e.g. { {2.4}, {5}, {6}, {2.4, 5}, {2.4, 6}, {5, 6} }. 1243 * 1244 * During the creation of candidate list, members which have same MLD AP MAC address are grouped 1245 * together. Let's say we have the following multi link candidates in one group {C_2.4, C_5, 1246 * C_6}. First intersect this list with allowed combination to get a collection like this, 1247 * { {C_2.4}, {C_5}, {C_6}, {C_2.4, C_5}, {C_2.4, C_6}, {C_5, C_6} }. For each of the sub-group, 1248 * predicted single link throughputs are added and each candidate in the subgroup get an 1249 * updated multi link throughput if the saved value is less. This calculation takes care of 1250 * eMLSR and STR. 1251 * 1252 * If the chip can't support all the radios for multi-link operation at the same time for STR 1253 * operation, we can't use the higher-order radio combinations. 1254 * 1255 * Above algorithm is extendable to multiple links with any number of bands and link 1256 * restriction. 1257 * 1258 * @param wifiCandidates A list of WifiCandidates 1259 */ updateMultiLinkCandidatesThroughput(WifiCandidates wifiCandidates)1260 private void updateMultiLinkCandidatesThroughput(WifiCandidates wifiCandidates) { 1261 ClientModeManager primaryManager = 1262 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 1263 if (primaryManager == null) return; 1264 String interfaceName = primaryManager.getInterfaceName(); 1265 if (interfaceName == null) return; 1266 1267 // Check if the chip has more than one MLO STR link support. 1268 int maxMloStrLinkCount = mWifiNative.getMaxMloStrLinkCount(interfaceName); 1269 if (maxMloStrLinkCount <= 1) return; 1270 1271 Set<List<Integer>> simultaneousBandCombinations = mWifiNative.getSupportedBandCombinations( 1272 interfaceName); 1273 if (simultaneousBandCombinations == null) return; 1274 1275 for (List<WifiCandidates.Candidate> mlCandidates : 1276 wifiCandidates.getMultiLinkCandidates()) { 1277 if (!isWifi7Enabled(mlCandidates)) continue; 1278 for (List<Integer> bands : simultaneousBandCombinations) { 1279 // Limit the radios/bands to maximum STR link supported in multi link operation. 1280 if (bands.size() > maxMloStrLinkCount) break; 1281 List<Integer> strBandsToIntersect = new ArrayList<>(bands); 1282 List<WifiCandidates.Candidate> strMlCandidates = intersectMlCandidatesWithStrBands( 1283 mlCandidates, strBandsToIntersect); 1284 if (strMlCandidates != null) { 1285 aggregateStrMultiLinkThroughput(strMlCandidates); 1286 } 1287 } 1288 } 1289 } 1290 1291 /** 1292 * Return the intersection of STR band combinations and best Multi-Link Wi-Fi candidates. 1293 */ intersectMlCandidatesWithStrBands( @onNull List<WifiCandidates.Candidate> candidates, @NonNull List<Integer> bands)1294 private List<WifiCandidates.Candidate> intersectMlCandidatesWithStrBands( 1295 @NonNull List<WifiCandidates.Candidate> candidates, @NonNull List<Integer> bands) { 1296 // Sorting is needed here to make the best candidates first in the list. 1297 List<WifiCandidates.Candidate> intersectedCandidates = candidates.stream() 1298 .sorted(Comparator.comparingInt( 1299 WifiCandidates.Candidate::getPredictedThroughputMbps).reversed()) 1300 .filter(k -> { 1301 int band = Integer.valueOf(ScanResult.toBand(k.getFrequency())); 1302 if (bands.contains(band)) { 1303 // Remove first occurrence as it is counted already. 1304 bands.remove(bands.indexOf(band)); 1305 return true; 1306 } 1307 return false; 1308 }) 1309 .collect(Collectors.toList()); 1310 // Make sure all bands are intersected. 1311 return (bands.isEmpty()) ? intersectedCandidates : null; 1312 } 1313 1314 /** 1315 * Aggregate the throughput of STR multi-link candidates. 1316 */ aggregateStrMultiLinkThroughput( @onNull List<WifiCandidates.Candidate> candidates)1317 private void aggregateStrMultiLinkThroughput( 1318 @NonNull List<WifiCandidates.Candidate> candidates) { 1319 // Add all throughputs. 1320 int predictedMlThroughput = candidates.stream() 1321 .mapToInt(c -> c.getPredictedThroughputMbps()) 1322 .sum(); 1323 // Check if an update needed for multi link throughput. 1324 candidates.stream() 1325 .filter(c -> c.getPredictedMultiLinkThroughputMbps() < predictedMlThroughput) 1326 .forEach(c -> c.setPredictedMultiLinkThroughputMbps(predictedMlThroughput)); 1327 } 1328 1329 /** 1330 * Add all results as candidates for the user selected network and let network selection 1331 * chooses the proper one for the user selected network. 1332 * @param config The configuration for the user selected network. 1333 * @param scanDetails List of ScanDetail for the user selected network. 1334 * @return list of valid Candidate(s) 1335 */ getCandidatesForUserSelection( WifiConfiguration config, @NonNull List<ScanDetail> scanDetails)1336 public List<WifiCandidates.Candidate> getCandidatesForUserSelection( 1337 WifiConfiguration config, @NonNull List<ScanDetail> scanDetails) { 1338 if (scanDetails.size() == 0) { 1339 if (mVerboseLoggingEnabled) { 1340 Log.d(TAG, "No scan result for the user selected network."); 1341 return null; 1342 } 1343 } 1344 1345 mConnectableNetworks.clear(); 1346 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext); 1347 for (ScanDetail scanDetail: scanDetails) { 1348 WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig( 1349 scanDetail, config); 1350 if (null == key) continue; 1351 1352 boolean added = wifiCandidates.add(key, config, 1353 WifiNetworkSelector.NetworkNominator.NOMINATOR_ID_CURRENT, 1354 scanDetail.getScanResult().level, 1355 scanDetail.getScanResult().frequency, 1356 scanDetail.getScanResult().channelWidth, 1357 0.0 /* lastSelectionWeightBetweenZeroAndOne */, 1358 false /* isMetered */, 1359 WifiNetworkSelector.isFromCarrierOrPrivilegedApp(config), 1360 predictThroughput(scanDetail), scanDetail.getScanResult().getApMldMacAddress()); 1361 if (!added) continue; 1362 1363 mConnectableNetworks.add(Pair.create(scanDetail, config)); 1364 mWifiConfigManager.updateScanDetailForNetwork( 1365 config.networkId, scanDetail); 1366 } 1367 return wifiCandidates.getCandidates(); 1368 } 1369 1370 /** 1371 * For transition networks with only legacy networks, 1372 * remove auto-upgrade type to use the legacy type to 1373 * avoid roaming issues between two types. 1374 */ removeAutoUpgradeSecurityParamsIfNecessary( WifiConfiguration config, List<SecurityParams> scanResultParamsList, @WifiConfiguration.SecurityType int baseSecurityType, @WifiConfiguration.SecurityType int upgradableSecurityType, boolean isLegacyNetworkInRange, boolean isUpgradableTypeOnlyInRange, boolean isAutoUpgradeEnabled)1375 private void removeAutoUpgradeSecurityParamsIfNecessary( 1376 WifiConfiguration config, 1377 List<SecurityParams> scanResultParamsList, 1378 @WifiConfiguration.SecurityType int baseSecurityType, 1379 @WifiConfiguration.SecurityType int upgradableSecurityType, 1380 boolean isLegacyNetworkInRange, 1381 boolean isUpgradableTypeOnlyInRange, 1382 boolean isAutoUpgradeEnabled) { 1383 localLog("removeAutoUpgradeSecurityParamsIfNecessary:" 1384 + " SSID: " + config.SSID 1385 + " baseSecurityType: " + baseSecurityType 1386 + " upgradableSecurityType: " + upgradableSecurityType 1387 + " isLegacyNetworkInRange: " + isLegacyNetworkInRange 1388 + " isUpgradableTypeOnlyInRange: " + isUpgradableTypeOnlyInRange 1389 + " isAutoUpgradeEnabled: " + isAutoUpgradeEnabled); 1390 if (isAutoUpgradeEnabled) { 1391 // Consider removing the auto-upgraded type if legacy networks are in range. 1392 if (!isLegacyNetworkInRange) return; 1393 // If base params is disabled or removed, keep the auto-upgrade params. 1394 SecurityParams baseParams = config.getSecurityParams(baseSecurityType); 1395 if (null == baseParams || !baseParams.isEnabled()) return; 1396 // If there are APs with standalone-upgradeable security type is in range, 1397 // do not consider removing the auto-upgraded type. 1398 if (isUpgradableTypeOnlyInRange) return; 1399 } 1400 1401 SecurityParams upgradableParams = config.getSecurityParams(upgradableSecurityType); 1402 if (null == upgradableParams) return; 1403 if (!upgradableParams.isAddedByAutoUpgrade()) return; 1404 localLog("Remove upgradable security type " + upgradableSecurityType + " for the network."); 1405 scanResultParamsList.removeIf(p -> p.isSecurityType(upgradableSecurityType)); 1406 } 1407 1408 /** Helper function to place all conditions which need to remove auto-upgrade types. */ removeSecurityParamsIfNecessary( WifiConfiguration config, List<SecurityParams> scanResultParamsList)1409 private void removeSecurityParamsIfNecessary( 1410 WifiConfiguration config, 1411 List<SecurityParams> scanResultParamsList) { 1412 // When offload is supported, both types are passed down. 1413 if (!mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()) { 1414 removeAutoUpgradeSecurityParamsIfNecessary( 1415 config, scanResultParamsList, 1416 WifiConfiguration.SECURITY_TYPE_PSK, 1417 WifiConfiguration.SECURITY_TYPE_SAE, 1418 mScanRequestProxy.isWpa2PersonalOnlyNetworkInRange(config.SSID), 1419 mScanRequestProxy.isWpa3PersonalOnlyNetworkInRange(config.SSID), 1420 mWifiGlobals.isWpa3SaeUpgradeEnabled()); 1421 } 1422 removeAutoUpgradeSecurityParamsIfNecessary( 1423 config, scanResultParamsList, 1424 WifiConfiguration.SECURITY_TYPE_OPEN, 1425 WifiConfiguration.SECURITY_TYPE_OWE, 1426 mScanRequestProxy.isOpenOnlyNetworkInRange(config.SSID), 1427 mScanRequestProxy.isOweOnlyNetworkInRange(config.SSID), 1428 mWifiGlobals.isOweUpgradeEnabled()); 1429 removeAutoUpgradeSecurityParamsIfNecessary( 1430 config, scanResultParamsList, 1431 WifiConfiguration.SECURITY_TYPE_EAP, 1432 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, 1433 mScanRequestProxy.isWpa2EnterpriseOnlyNetworkInRange(config.SSID), 1434 mScanRequestProxy.isWpa3EnterpriseOnlyNetworkInRange(config.SSID), 1435 true); 1436 // When using WPA3 (SAE), all passwords in all lengths are strings, but when using WPA2, 1437 // there is a distinction between 8-63 octets that go through BDKDF2 function, and 1438 // 64-octets that are assumed to be the output of it. BDKDF2 is not applicable to SAE 1439 // and to prevent interop issues with APs when 64-octet Hex PSK is configured, update 1440 // the configuration to use WPA2 only. 1441 WifiConfiguration configWithPassword = mWifiConfigManager 1442 .getConfiguredNetworkWithPassword(config.networkId); 1443 if (configWithPassword.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 1444 && configWithPassword.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 1445 && configWithPassword.preSharedKey != null 1446 && !configWithPassword.preSharedKey.startsWith("\"") 1447 && configWithPassword.preSharedKey.length() == 64 1448 && configWithPassword.preSharedKey.matches("[0-9A-Fa-f]{64}")) { 1449 localLog("Remove SAE type for " + configWithPassword.SSID + " with 64-octet Hex PSK."); 1450 scanResultParamsList 1451 .removeIf(p -> p.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)); 1452 } 1453 } 1454 1455 /** 1456 * For the transition mode, MFPC should be true, and MFPR should be false, 1457 * see WPA3 SAE specification section 2.3 and 3.3. 1458 */ updateSecurityParamsForTransitionModeIfNecessary( ScanResult scanResult, SecurityParams params)1459 private void updateSecurityParamsForTransitionModeIfNecessary( 1460 ScanResult scanResult, SecurityParams params) { 1461 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 1462 && params.isAddedByAutoUpgrade() 1463 && ScanResultUtil.isScanResultForPskSaeTransitionNetwork(scanResult)) { 1464 params.setRequirePmf(false); 1465 } else if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE) 1466 && params.isAddedByAutoUpgrade() 1467 && ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)) { 1468 params.setRequirePmf(false); 1469 } 1470 } 1471 1472 /** 1473 * Update the candidate security params against a scan detail. 1474 * 1475 * @param network the target network. 1476 * @param scanDetail the target scan detail. 1477 */ updateNetworkCandidateSecurityParams( WifiConfiguration network, ScanDetail scanDetail)1478 private void updateNetworkCandidateSecurityParams( 1479 WifiConfiguration network, ScanDetail scanDetail) { 1480 if (network == null) return; 1481 if (scanDetail == null) return; 1482 1483 ScanResult scanResult = scanDetail.getScanResult(); 1484 List<SecurityParams> scanResultParamsList = ScanResultUtil 1485 .generateSecurityParamsListFromScanResult(scanResult); 1486 if (scanResultParamsList == null) return; 1487 // Under some conditions, the legacy type is preferred to have better 1488 // connectivity behaviors, and the auto-upgrade type should be removed. 1489 removeSecurityParamsIfNecessary(network, scanResultParamsList); 1490 SecurityParams params = ScanResultMatchInfo.getBestMatchingSecurityParams( 1491 network, 1492 scanResultParamsList); 1493 if (params == null) return; 1494 updateSecurityParamsForTransitionModeIfNecessary(scanResult, params); 1495 mWifiConfigManager.setNetworkCandidateScanResult( 1496 network.networkId, scanResult, 0, params); 1497 } 1498 1499 /** 1500 * Using the registered Scorers, choose the best network from the list of Candidate(s). 1501 * The ScanDetailCache is also updated here. 1502 * @param candidates - Candidates to perferm network selection on. 1503 * @return WifiConfiguration - the selected network, or null. 1504 */ 1505 @Nullable selectNetwork(@onNull List<WifiCandidates.Candidate> candidates)1506 public WifiConfiguration selectNetwork(@NonNull List<WifiCandidates.Candidate> candidates) { 1507 return selectNetwork(candidates, true); 1508 } 1509 1510 /** 1511 * Using the registered Scorers, choose the best network from the list of Candidate(s). 1512 * The ScanDetailCache is also updated here. 1513 * @param candidates - Candidates to perform network selection on. 1514 * @param overrideEnabled If it is allowed to override candidate with User Connect Choice. 1515 * @return WifiConfiguration - the selected network, or null. 1516 */ 1517 @Nullable selectNetwork(@onNull List<WifiCandidates.Candidate> candidates, boolean overrideEnabled)1518 public WifiConfiguration selectNetwork(@NonNull List<WifiCandidates.Candidate> candidates, 1519 boolean overrideEnabled) { 1520 if (candidates == null || candidates.size() == 0) { 1521 return null; 1522 } 1523 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext, candidates); 1524 final WifiCandidates.CandidateScorer activeScorer = getActiveCandidateScorer(); 1525 // Update the NetworkSelectionStatus in the configs for the current candidates 1526 // This is needed for the legacy user connect choice, at least 1527 Collection<Collection<WifiCandidates.Candidate>> groupedCandidates = 1528 wifiCandidates.getGroupedCandidates(); 1529 for (Collection<WifiCandidates.Candidate> group : groupedCandidates) { 1530 WifiCandidates.ScoredCandidate choice = activeScorer.scoreCandidates(group); 1531 if (choice == null) continue; 1532 ScanDetail scanDetail = getScanDetailForCandidateKey(choice.candidateKey); 1533 if (scanDetail == null) continue; 1534 WifiConfiguration config = mWifiConfigManager 1535 .getConfiguredNetwork(choice.candidateKey.networkId); 1536 if (config == null) continue; 1537 updateNetworkCandidateSecurityParams(config, scanDetail); 1538 } 1539 1540 for (Collection<WifiCandidates.Candidate> group : groupedCandidates) { 1541 for (WifiCandidates.Candidate candidate : group.stream() 1542 .sorted((a, b) -> (b.getScanRssi() - a.getScanRssi())) // decreasing rssi 1543 .collect(Collectors.toList())) { 1544 localLog(candidate.toString()); 1545 } 1546 } 1547 1548 ArrayMap<Integer, Integer> experimentNetworkSelections = new ArrayMap<>(); // for metrics 1549 1550 int selectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 1551 1552 // Run all the CandidateScorers 1553 boolean legacyOverrideWanted = true; 1554 for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) { 1555 WifiCandidates.ScoredCandidate choice; 1556 try { 1557 choice = wifiCandidates.choose(candidateScorer); 1558 } catch (RuntimeException e) { 1559 Log.wtf(TAG, "Exception running a CandidateScorer", e); 1560 continue; 1561 } 1562 int networkId = choice.candidateKey == null 1563 ? WifiConfiguration.INVALID_NETWORK_ID 1564 : choice.candidateKey.networkId; 1565 String chooses = " would choose "; 1566 if (candidateScorer == activeScorer) { 1567 chooses = " chooses "; 1568 legacyOverrideWanted = choice.userConnectChoiceOverride; 1569 selectedNetworkId = networkId; 1570 updateChosenPasspointNetwork(choice); 1571 } 1572 String id = candidateScorer.getIdentifier(); 1573 int expid = experimentIdFromIdentifier(id); 1574 localLog(id + chooses + networkId 1575 + " score " + choice.value + "+/-" + choice.err 1576 + " expid " + expid); 1577 experimentNetworkSelections.put(expid, networkId); 1578 } 1579 1580 // Update metrics about differences in the selections made by various methods 1581 final int activeExperimentId = experimentIdFromIdentifier(activeScorer.getIdentifier()); 1582 for (Map.Entry<Integer, Integer> entry : 1583 experimentNetworkSelections.entrySet()) { 1584 int experimentId = entry.getKey(); 1585 if (experimentId == activeExperimentId) continue; 1586 int thisSelectedNetworkId = entry.getValue(); 1587 mWifiMetrics.logNetworkSelectionDecision(experimentId, activeExperimentId, 1588 selectedNetworkId == thisSelectedNetworkId, 1589 groupedCandidates.size()); 1590 } 1591 1592 // Get a fresh copy of WifiConfiguration reflecting any scan result updates 1593 WifiConfiguration selectedNetwork = 1594 mWifiConfigManager.getConfiguredNetwork(selectedNetworkId); 1595 if (selectedNetwork != null && legacyOverrideWanted && overrideEnabled 1596 && mUserConnectChoiceOverrideEnabled) { 1597 selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork); 1598 } 1599 if (selectedNetwork != null) { 1600 mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); 1601 } 1602 return selectedNetwork; 1603 } 1604 1605 /** 1606 * Returns the ScanDetail given the candidate key, using the saved list of connectible networks. 1607 */ getScanDetailForCandidateKey(WifiCandidates.Key candidateKey)1608 private ScanDetail getScanDetailForCandidateKey(WifiCandidates.Key candidateKey) { 1609 if (candidateKey == null) return null; 1610 String bssid = candidateKey.bssid.toString(); 1611 for (Pair<ScanDetail, WifiConfiguration> pair : mConnectableNetworks) { 1612 if (candidateKey.networkId == pair.second.networkId 1613 && bssid.equals(pair.first.getBSSIDString())) { 1614 return pair.first; 1615 } 1616 } 1617 return null; 1618 } 1619 updateChosenPasspointNetwork(WifiCandidates.ScoredCandidate choice)1620 private void updateChosenPasspointNetwork(WifiCandidates.ScoredCandidate choice) { 1621 if (choice.candidateKey == null) { 1622 return; 1623 } 1624 WifiConfiguration config = 1625 mWifiConfigManager.getConfiguredNetwork(choice.candidateKey.networkId); 1626 if (config == null) { 1627 return; 1628 } 1629 if (config.isPasspoint()) { 1630 config.SSID = choice.candidateKey.matchInfo.networkSsid; 1631 mWifiConfigManager.addOrUpdateNetwork(config, config.creatorUid, config.creatorName, 1632 false); 1633 } 1634 } 1635 updateScanDetailCache(List<ScanDetail> scanDetails)1636 private void updateScanDetailCache(List<ScanDetail> scanDetails) { 1637 for (ScanDetail scanDetail : scanDetails) { 1638 mWifiConfigManager.updateScanDetailCacheFromScanDetailForSavedNetwork(scanDetail); 1639 } 1640 } 1641 toProtoNominatorId(@etworkNominator.NominatorId int nominatorId)1642 private static int toProtoNominatorId(@NetworkNominator.NominatorId int nominatorId) { 1643 switch (nominatorId) { 1644 case NetworkNominator.NOMINATOR_ID_SAVED: 1645 return WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED; 1646 case NetworkNominator.NOMINATOR_ID_SUGGESTION: 1647 return WifiMetricsProto.ConnectionEvent.NOMINATOR_SUGGESTION; 1648 case NetworkNominator.NOMINATOR_ID_SCORED: 1649 return WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED; 1650 case NetworkNominator.NOMINATOR_ID_CURRENT: 1651 Log.e(TAG, "Unexpected NOMINATOR_ID_CURRENT", new RuntimeException()); 1652 return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN; 1653 default: 1654 Log.e(TAG, "UnrecognizedNominatorId" + nominatorId); 1655 return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN; 1656 } 1657 } 1658 calculateLastSelectionWeight(int networkId, boolean isMetered)1659 private double calculateLastSelectionWeight(int networkId, boolean isMetered) { 1660 if (!mLastSelectionWeightEnabled 1661 || networkId != mWifiConfigManager.getLastSelectedNetwork()) { 1662 return 0.0; 1663 } 1664 double timeDifference = mClock.getElapsedSinceBootMillis() 1665 - mWifiConfigManager.getLastSelectedTimeStamp(); 1666 long millis = TimeUnit.MINUTES.toMillis(isMetered 1667 ? mScoringParams.getLastMeteredSelectionMinutes() 1668 : mScoringParams.getLastUnmeteredSelectionMinutes()); 1669 if (timeDifference >= millis) return 0.0; 1670 double unclipped = 1.0 - (timeDifference / millis); 1671 return Math.min(Math.max(unclipped, 0.0), 1.0); 1672 } 1673 getActiveCandidateScorer()1674 private WifiCandidates.CandidateScorer getActiveCandidateScorer() { 1675 WifiCandidates.CandidateScorer ans = mCandidateScorers.get(PRESET_CANDIDATE_SCORER_NAME); 1676 int overrideExperimentId = mScoringParams.getExperimentIdentifier(); 1677 if (overrideExperimentId >= MIN_SCORER_EXP_ID) { 1678 for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) { 1679 int expId = experimentIdFromIdentifier(candidateScorer.getIdentifier()); 1680 if (expId == overrideExperimentId) { 1681 ans = candidateScorer; 1682 break; 1683 } 1684 } 1685 } 1686 if (ans == null && PRESET_CANDIDATE_SCORER_NAME != null) { 1687 Log.wtf(TAG, PRESET_CANDIDATE_SCORER_NAME + " is not registered!"); 1688 } 1689 mWifiMetrics.setNetworkSelectorExperimentId(ans == null 1690 ? LEGACY_CANDIDATE_SCORER_EXP_ID 1691 : experimentIdFromIdentifier(ans.getIdentifier())); 1692 return ans; 1693 } 1694 predictThroughput(@onNull ScanDetail scanDetail)1695 private int predictThroughput(@NonNull ScanDetail scanDetail) { 1696 if (scanDetail.getScanResult() == null || scanDetail.getNetworkDetail() == null) { 1697 return 0; 1698 } 1699 int channelUtilizationLinkLayerStats = BssLoad.INVALID; 1700 if (mWifiChannelUtilization != null) { 1701 channelUtilizationLinkLayerStats = 1702 mWifiChannelUtilization.getUtilizationRatio( 1703 scanDetail.getScanResult().frequency); 1704 } 1705 ClientModeManager primaryManager = 1706 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 1707 return mThroughputPredictor.predictThroughput( 1708 primaryManager.getDeviceWiphyCapabilities(), 1709 scanDetail.getScanResult().getWifiStandard(), 1710 scanDetail.getScanResult().channelWidth, 1711 scanDetail.getScanResult().level, 1712 scanDetail.getScanResult().frequency, 1713 scanDetail.getNetworkDetail().getMaxNumberSpatialStreams(), 1714 scanDetail.getNetworkDetail().getChannelUtilization(), 1715 channelUtilizationLinkLayerStats, 1716 mWifiGlobals.isBluetoothConnected(), 1717 scanDetail.getNetworkDetail().getDisabledSubchannelBitmap()); 1718 } 1719 1720 /** 1721 * Register a network nominator 1722 * 1723 * @param nominator the network nominator to be registered 1724 */ registerNetworkNominator(@onNull NetworkNominator nominator)1725 public void registerNetworkNominator(@NonNull NetworkNominator nominator) { 1726 mNominators.add(Preconditions.checkNotNull(nominator)); 1727 } 1728 1729 /** 1730 * Register a candidate scorer. 1731 * 1732 * Replaces any existing scorer having the same identifier. 1733 */ registerCandidateScorer(@onNull WifiCandidates.CandidateScorer candidateScorer)1734 public void registerCandidateScorer(@NonNull WifiCandidates.CandidateScorer candidateScorer) { 1735 String name = Preconditions.checkNotNull(candidateScorer).getIdentifier(); 1736 if (name != null) { 1737 mCandidateScorers.put(name, candidateScorer); 1738 } 1739 } 1740 1741 /** 1742 * Unregister a candidate scorer. 1743 */ unregisterCandidateScorer(@onNull WifiCandidates.CandidateScorer candidateScorer)1744 public void unregisterCandidateScorer(@NonNull WifiCandidates.CandidateScorer candidateScorer) { 1745 String name = Preconditions.checkNotNull(candidateScorer).getIdentifier(); 1746 if (name != null) { 1747 mCandidateScorers.remove(name); 1748 } 1749 } 1750 1751 /** 1752 * Indicate whether or not a configuration is from carrier or privileged app. 1753 * 1754 * @param config The network configuration 1755 * @return true if this configuration is from carrier or privileged app; false otherwise. 1756 */ isFromCarrierOrPrivilegedApp(WifiConfiguration config)1757 public static boolean isFromCarrierOrPrivilegedApp(WifiConfiguration config) { 1758 if (config.fromWifiNetworkSuggestion 1759 && config.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 1760 // Privileged carrier suggestion 1761 return true; 1762 } 1763 if (config.isEphemeral() 1764 && !config.fromWifiNetworkSpecifier 1765 && !config.fromWifiNetworkSuggestion) { 1766 // From ScoredNetworkNominator 1767 return true; 1768 } 1769 return false; 1770 } 1771 1772 /** 1773 * Derives a numeric experiment identifier from a CandidateScorer's identifier. 1774 * 1775 * @returns a positive number that starts with the decimal digits ID_PREFIX 1776 */ experimentIdFromIdentifier(String id)1777 public static int experimentIdFromIdentifier(String id) { 1778 final int digits = (int) (((long) id.hashCode()) & Integer.MAX_VALUE) % ID_SUFFIX_MOD; 1779 return ID_PREFIX * ID_SUFFIX_MOD + digits; 1780 } 1781 1782 private static final int ID_SUFFIX_MOD = 1_000_000; 1783 private static final int ID_PREFIX = 42; 1784 private static final int MIN_SCORER_EXP_ID = ID_PREFIX * ID_SUFFIX_MOD; 1785 WifiNetworkSelector( Context context, WifiScoreCard wifiScoreCard, ScoringParams scoringParams, WifiConfigManager configManager, Clock clock, LocalLog localLog, WifiMetrics wifiMetrics, WifiInjector wifiInjector, ThroughputPredictor throughputPredictor, WifiChannelUtilization wifiChannelUtilization, WifiGlobals wifiGlobals, ScanRequestProxy scanRequestProxy, WifiNative wifiNative)1786 WifiNetworkSelector( 1787 Context context, 1788 WifiScoreCard wifiScoreCard, 1789 ScoringParams scoringParams, 1790 WifiConfigManager configManager, 1791 Clock clock, 1792 LocalLog localLog, 1793 WifiMetrics wifiMetrics, 1794 WifiInjector wifiInjector, 1795 ThroughputPredictor throughputPredictor, 1796 WifiChannelUtilization wifiChannelUtilization, 1797 WifiGlobals wifiGlobals, 1798 ScanRequestProxy scanRequestProxy, 1799 WifiNative wifiNative) { 1800 mContext = context; 1801 mWifiScoreCard = wifiScoreCard; 1802 mScoringParams = scoringParams; 1803 mWifiConfigManager = configManager; 1804 mClock = clock; 1805 mLocalLog = localLog; 1806 mWifiMetrics = wifiMetrics; 1807 mWifiInjector = wifiInjector; 1808 mThroughputPredictor = throughputPredictor; 1809 mWifiChannelUtilization = wifiChannelUtilization; 1810 mWifiGlobals = wifiGlobals; 1811 mScanRequestProxy = scanRequestProxy; 1812 mWifiNative = wifiNative; 1813 mDevicePolicyManager = WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext); 1814 } 1815 } 1816