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, int autojoinRestrictionSecurityTypes)469 private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails, 470 Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates, 471 int autojoinRestrictionSecurityTypes) { 472 List<ScanDetail> validScanDetails = new ArrayList<>(); 473 StringBuffer noValidSsid = new StringBuffer(); 474 StringBuffer blockedBssid = new StringBuffer(); 475 StringBuffer lowRssi = new StringBuffer(); 476 StringBuffer mboAssociationDisallowedBssid = new StringBuffer(); 477 StringBuffer adminRestrictedSsid = new StringBuffer(); 478 StringJoiner deprecatedSecurityTypeSsid = new StringJoiner(" / "); 479 StringJoiner autojoinRestrictionSecurityTypesBssid = new StringJoiner(" / "); 480 List<String> currentBssids = cmmStates.stream() 481 .map(cmmState -> cmmState.wifiInfo.getBSSID()) 482 .collect(Collectors.toList()); 483 Set<String> scanResultPresentForCurrentBssids = new ArraySet<>(); 484 485 int adminMinimumSecurityLevel = 0; 486 boolean adminSsidRestrictionSet = false; 487 Set<WifiSsid> adminSsidAllowlist = new ArraySet<>(); 488 Set<WifiSsid> admindSsidDenylist = new ArraySet<>(); 489 490 int numBssidFiltered = 0; 491 492 if (mDevicePolicyManager != null && SdkLevel.isAtLeastT()) { 493 adminMinimumSecurityLevel = 494 mDevicePolicyManager.getMinimumRequiredWifiSecurityLevel(); 495 WifiSsidPolicy policy = mDevicePolicyManager.getWifiSsidPolicy(); 496 if (policy != null) { 497 adminSsidRestrictionSet = true; 498 if (policy.getPolicyType() == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) { 499 adminSsidAllowlist = policy.getSsids(); 500 } else { 501 admindSsidDenylist = policy.getSsids(); 502 } 503 } 504 } 505 506 for (ScanDetail scanDetail : scanDetails) { 507 ScanResult scanResult = scanDetail.getScanResult(); 508 509 if (TextUtils.isEmpty(scanResult.SSID)) { 510 noValidSsid.append(scanResult.BSSID).append(" / "); 511 continue; 512 } 513 514 // Check if the scan results contain the currently connected BSSID's 515 if (currentBssids.contains(scanResult.BSSID)) { 516 scanResultPresentForCurrentBssids.add(scanResult.BSSID); 517 validScanDetails.add(scanDetail); 518 continue; 519 } 520 521 final String scanId = toScanId(scanResult); 522 523 if (bssidBlocklist.contains(scanResult.BSSID)) { 524 blockedBssid.append(scanId).append(" / "); 525 numBssidFiltered++; 526 continue; 527 } 528 529 // Skip network with too weak signals. 530 if (isSignalTooWeak(scanResult)) { 531 lowRssi.append(scanId); 532 if (scanResult.is24GHz()) { 533 lowRssi.append("(2.4GHz)"); 534 } else if (scanResult.is5GHz()) { 535 lowRssi.append("(5GHz)"); 536 } else if (scanResult.is6GHz()) { 537 lowRssi.append("(6GHz)"); 538 } 539 lowRssi.append(scanResult.level).append(" / "); 540 continue; 541 } 542 543 // Skip BSS which is not accepting new connections. 544 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 545 if (networkDetail != null) { 546 if (networkDetail.getMboAssociationDisallowedReasonCode() 547 != MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT) { 548 mWifiMetrics 549 .incrementNetworkSelectionFilteredBssidCountDueToMboAssocDisallowInd(); 550 mboAssociationDisallowedBssid.append(scanId).append("(") 551 .append(networkDetail.getMboAssociationDisallowedReasonCode()) 552 .append(")").append(" / "); 553 continue; 554 } 555 } 556 557 // Skip network that does not meet the admin set SSID restriction 558 if (adminSsidRestrictionSet) { 559 WifiSsid ssid = scanResult.getWifiSsid(); 560 // Allowlist policy set but network is not present in the list 561 if (!adminSsidAllowlist.isEmpty() && !adminSsidAllowlist.contains(ssid)) { 562 adminRestrictedSsid.append(scanId).append(" / "); 563 continue; 564 } 565 // Denylist policy set but network is present in the list 566 if (!admindSsidDenylist.isEmpty() && admindSsidDenylist.contains(ssid)) { 567 adminRestrictedSsid.append(scanId).append(" / "); 568 continue; 569 } 570 } 571 572 // Skip network that does not meet the admin set minimum security level restriction 573 if (adminMinimumSecurityLevel != 0) { 574 boolean securityRestrictionPassed = false; 575 @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); 576 for (int type : securityTypes) { 577 int securityLevel = WifiInfo.convertSecurityTypeToDpmWifiSecurity(type); 578 579 // Skip unknown security type since security level cannot be determined. 580 // If all the security types are unknown when the minimum security level 581 // restriction is set, the scan result is ignored. 582 if (securityLevel == WifiInfo.DPM_SECURITY_TYPE_UNKNOWN) continue; 583 584 if (adminMinimumSecurityLevel <= securityLevel) { 585 securityRestrictionPassed = true; 586 break; 587 } 588 } 589 if (!securityRestrictionPassed) { 590 adminRestrictedSsid.append(scanId).append(" / "); 591 continue; 592 } 593 } 594 595 // Skip network that has deprecated security type 596 if (mWifiGlobals.isWpaPersonalDeprecated() || mWifiGlobals.isWepDeprecated()) { 597 boolean securityTypeDeprecated = false; 598 @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); 599 for (int type : securityTypes) { 600 if (mWifiGlobals.isWepDeprecated() && type == WifiInfo.SECURITY_TYPE_WEP) { 601 securityTypeDeprecated = true; 602 break; 603 } 604 if (mWifiGlobals.isWpaPersonalDeprecated() && type == WifiInfo.SECURITY_TYPE_PSK 605 && ScanResultUtil.isScanResultForWpaPersonalOnlyNetwork(scanResult)) { 606 securityTypeDeprecated = true; 607 break; 608 } 609 } 610 if (securityTypeDeprecated) { 611 deprecatedSecurityTypeSsid.add(scanId); 612 continue; 613 } 614 } 615 616 // Skip network with security type that is restricted to auto join 617 if (autojoinRestrictionSecurityTypes != 0/*restrict none*/) { 618 @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); 619 boolean securityTypeRestricted = true; 620 for (int type : securityTypes) { 621 if (((0x1 << type) & autojoinRestrictionSecurityTypes) == 0) { 622 securityTypeRestricted = false; 623 break; 624 } 625 } 626 if (securityTypeRestricted) { 627 autojoinRestrictionSecurityTypesBssid.add(scanId); 628 continue; 629 } 630 } 631 632 validScanDetails.add(scanDetail); 633 } 634 mWifiMetrics.incrementNetworkSelectionFilteredBssidCount(numBssidFiltered); 635 636 // WNS listens to all single scan results. Some scan requests may not include 637 // the channel of the currently connected network, so the currently connected 638 // network won't show up in the scan results. We don't act on these scan results 639 // to avoid aggressive network switching which might trigger disconnection. 640 // TODO(b/147751334) this may no longer be needed 641 for (ClientModeManagerState cmmState : cmmStates) { 642 // TODO (b/169413079): Disable network selection on corresponding CMM instead. 643 if (cmmState.connected && cmmState.wifiInfo.getScore() >= WIFI_POOR_SCORE 644 && !scanResultPresentForCurrentBssids.contains(cmmState.wifiInfo.getBSSID())) { 645 if (isSufficiencyCheckEnabled()) { 646 localLog("Current connected BSSID " + cmmState.wifiInfo.getBSSID() 647 + " is not in the scan results. Skip network selection."); 648 validScanDetails.clear(); 649 return validScanDetails; 650 } else { 651 localLog("Current connected BSSID " + cmmState.wifiInfo.getBSSID() 652 + " is not in the scan results. But continue network selection because" 653 + " sufficiency check is disabled."); 654 } 655 } 656 } 657 658 if (noValidSsid.length() != 0) { 659 localLog("Networks filtered out due to invalid SSID: " + noValidSsid); 660 } 661 662 if (blockedBssid.length() != 0) { 663 localLog("Networks filtered out due to blocklist: " + blockedBssid); 664 } 665 666 if (lowRssi.length() != 0) { 667 localLog("Networks filtered out due to low signal strength: " + lowRssi); 668 } 669 670 if (mboAssociationDisallowedBssid.length() != 0) { 671 localLog("Networks filtered out due to mbo association disallowed indication: " 672 + mboAssociationDisallowedBssid); 673 } 674 675 if (adminRestrictedSsid.length() != 0) { 676 localLog("Networks filtered out due to admin restrictions: " + adminRestrictedSsid); 677 } 678 679 if (deprecatedSecurityTypeSsid.length() != 0) { 680 localLog("Networks filtered out due to deprecated security type: " 681 + deprecatedSecurityTypeSsid); 682 } 683 684 if (autojoinRestrictionSecurityTypesBssid.length() != 0) { 685 localLog("Networks filtered out due to auto join restriction on the security type: " 686 + autojoinRestrictionSecurityTypesBssid); 687 } 688 689 return validScanDetails; 690 } 691 findScanDetailForBssid(List<ScanDetail> scanDetails, String currentBssid)692 private ScanDetail findScanDetailForBssid(List<ScanDetail> scanDetails, 693 String currentBssid) { 694 for (ScanDetail scanDetail : scanDetails) { 695 ScanResult scanResult = scanDetail.getScanResult(); 696 if (scanResult.BSSID.equals(currentBssid)) { 697 return scanDetail; 698 } 699 } 700 return null; 701 } 702 isEnhancedOpenSupported()703 private boolean isEnhancedOpenSupported() { 704 if (mIsEnhancedOpenSupportedInitialized) { 705 return mIsEnhancedOpenSupported; 706 } 707 708 mIsEnhancedOpenSupportedInitialized = true; 709 ClientModeManager primaryManager = 710 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 711 mIsEnhancedOpenSupported = primaryManager 712 .getSupportedFeaturesBitSet().get(WIFI_FEATURE_OWE); 713 return mIsEnhancedOpenSupported; 714 } 715 716 /** 717 * This returns a list of ScanDetails that were filtered in the process of network selection. 718 * The list is further filtered for only open unsaved networks. 719 * 720 * @return the list of ScanDetails for open unsaved networks that do not have invalid SSIDS, 721 * blocked BSSIDS, or low signal strength. This will return an empty list when there are 722 * no open unsaved networks, or when network selection has not been run. 723 */ getFilteredScanDetailsForOpenUnsavedNetworks()724 public List<ScanDetail> getFilteredScanDetailsForOpenUnsavedNetworks() { 725 List<ScanDetail> openUnsavedNetworks = new ArrayList<>(); 726 boolean enhancedOpenSupported = isEnhancedOpenSupported(); 727 for (ScanDetail scanDetail : mFilteredNetworks) { 728 ScanResult scanResult = scanDetail.getScanResult(); 729 730 if (!ScanResultUtil.isScanResultForOpenNetwork(scanResult)) { 731 continue; 732 } 733 734 // Filter out Enhanced Open networks on devices that do not support it 735 if (ScanResultUtil.isScanResultForOweNetwork(scanResult) 736 && !enhancedOpenSupported) { 737 continue; 738 } 739 740 // Skip saved networks 741 if (mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail) != null) { 742 continue; 743 } 744 745 openUnsavedNetworks.add(scanDetail); 746 } 747 return openUnsavedNetworks; 748 } 749 750 /** 751 * @return the list of ScanDetails scored as potential candidates by the last run of 752 * selectNetwork, this will be empty if Network selector determined no selection was 753 * needed on last run. This includes scan details of sufficient signal strength, and 754 * had an associated WifiConfiguration. 755 */ getConnectableScanDetails()756 public List<Pair<ScanDetail, WifiConfiguration>> getConnectableScanDetails() { 757 return mConnectableNetworks; 758 } 759 760 /** 761 * Iterate thru the list of configured networks (includes all saved network configurations + 762 * any ephemeral network configurations created for passpoint networks, suggestions, carrier 763 * networks, etc) and do the following: 764 * a) Try to re-enable any temporarily enabled networks (if the blocklist duration has expired). 765 * b) Clear the {@link WifiConfiguration.NetworkSelectionStatus#getCandidate()} field for all 766 * of them to identify networks that are present in the current scan result. 767 * c) Log any disabled networks. 768 */ updateConfiguredNetworks()769 private void updateConfiguredNetworks() { 770 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 771 if (configuredNetworks.size() == 0) { 772 localLog("No configured networks."); 773 return; 774 } 775 776 StringBuffer sbuf = new StringBuffer(); 777 for (WifiConfiguration network : configuredNetworks) { 778 // If a configuration is temporarily disabled, re-enable it before trying 779 // to connect to it. 780 mWifiConfigManager.tryEnableNetwork(network.networkId); 781 // Clear the cached candidate, score and seen. 782 mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId); 783 784 // Log disabled network. 785 WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus(); 786 if (!status.isNetworkEnabled()) { 787 sbuf.append(" ").append(toNetworkString(network)).append(" "); 788 for (int index = WifiConfiguration.NetworkSelectionStatus 789 .NETWORK_SELECTION_DISABLED_STARTING_INDEX; 790 index < WifiConfiguration.NetworkSelectionStatus 791 .NETWORK_SELECTION_DISABLED_MAX; 792 index++) { 793 int count = status.getDisableReasonCounter(index); 794 // Here we log the reason as long as its count is greater than zero. The 795 // network may not be disabled because of this particular reason. Logging 796 // this information anyway to help understand what happened to the network. 797 if (count > 0) { 798 sbuf.append("reason=") 799 .append(WifiConfiguration.NetworkSelectionStatus 800 .getNetworkSelectionDisableReasonString(index)) 801 .append(", count=").append(count).append("; "); 802 } 803 } 804 sbuf.append("\n"); 805 } 806 } 807 808 if (sbuf.length() > 0) { 809 localLog("Disabled configured networks:"); 810 localLog(sbuf.toString()); 811 } 812 } 813 814 /** 815 * Overrides the {@code candidate} chosen by the {@link #mNominators} with the user chosen 816 * {@link WifiConfiguration} if one exists. 817 * 818 * @return the user chosen {@link WifiConfiguration} if one exists, {@code candidate} otherwise 819 */ overrideCandidateWithUserConnectChoice( @onNull WifiConfiguration candidate)820 private WifiConfiguration overrideCandidateWithUserConnectChoice( 821 @NonNull WifiConfiguration candidate) { 822 WifiConfiguration tempConfig = Preconditions.checkNotNull(candidate); 823 WifiConfiguration originalCandidate = candidate; 824 ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); 825 826 Set<String> seenNetworks = new HashSet<>(); 827 seenNetworks.add(candidate.getProfileKey()); 828 829 while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) { 830 String key = tempConfig.getNetworkSelectionStatus().getConnectChoice(); 831 int userSelectedRssi = tempConfig.getNetworkSelectionStatus().getConnectChoiceRssi(); 832 tempConfig = mWifiConfigManager.getConfiguredNetwork(key); 833 834 if (tempConfig != null) { 835 if (seenNetworks.contains(tempConfig.getProfileKey())) { 836 Log.wtf(TAG, "user connected network is a loop, use candidate:" 837 + candidate); 838 if (candidate.getNetworkSelectionStatus().getCandidate() != null) { 839 mWifiConfigManager.setLegacyUserConnectChoice(candidate, 840 candidate.getNetworkSelectionStatus().getCandidate().level); 841 } 842 break; 843 } 844 seenNetworks.add(tempConfig.getProfileKey()); 845 WifiConfiguration.NetworkSelectionStatus tempStatus = 846 tempConfig.getNetworkSelectionStatus(); 847 boolean noInternetButInternetIsExpected = !tempConfig.isNoInternetAccessExpected() 848 && tempConfig.hasNoInternetAccess(); 849 if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled() 850 && !noInternetButInternetIsExpected 851 && isUserChoiceRssiCloseToOrGreaterThanExpectedValue( 852 tempStatus.getCandidate().level, userSelectedRssi)) { 853 scanResultCandidate = tempStatus.getCandidate(); 854 candidate = tempConfig; 855 } 856 } else { 857 localLog("Connect choice: " + key + " has no corresponding saved config."); 858 break; 859 } 860 } 861 862 if (candidate != originalCandidate) { 863 localLog("After user selection adjustment, the final candidate is:" 864 + WifiNetworkSelector.toNetworkString(candidate) + " : " 865 + scanResultCandidate.BSSID); 866 mWifiMetrics.setNominatorForNetwork(candidate.networkId, 867 WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED_USER_CONNECT_CHOICE); 868 } 869 return candidate; 870 } 871 isUserChoiceRssiCloseToOrGreaterThanExpectedValue(int observedRssi, int expectedRssi)872 private boolean isUserChoiceRssiCloseToOrGreaterThanExpectedValue(int observedRssi, 873 int expectedRssi) { 874 // The expectedRssi may be 0 for newly upgraded devices which do not have this information, 875 // pass the test for those devices to avoid regression. 876 if (expectedRssi == 0) { 877 return true; 878 } 879 return observedRssi >= expectedRssi - mScoringParams.getEstimateRssiErrorMargin(); 880 } 881 882 883 /** 884 * Indicates whether we have ever seen the network to be metered since wifi was enabled. 885 * 886 * This is sticky to prevent continuous flip-flopping between networks, when the metered 887 * status is learned after association. 888 */ isEverMetered(@onNull WifiConfiguration config, @Nullable WifiInfo info, @NonNull ScanDetail scanDetail)889 private boolean isEverMetered(@NonNull WifiConfiguration config, @Nullable WifiInfo info, 890 @NonNull ScanDetail scanDetail) { 891 // If info does not match config, don't use it. 892 if (info != null && info.getNetworkId() != config.networkId) info = null; 893 boolean metered = WifiConfiguration.isMetered(config, info); 894 NetworkDetail networkDetail = scanDetail.getNetworkDetail(); 895 if (networkDetail != null 896 && networkDetail.getAnt() 897 == NetworkDetail.Ant.ChargeablePublic) { 898 metered = true; 899 } 900 mWifiMetrics.addMeteredStat(config, metered); 901 if (config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE) { 902 // User override is in effect; we should trust it 903 if (mKnownMeteredNetworkIds.remove(config.networkId)) { 904 localLog("KnownMeteredNetworkIds = " + mKnownMeteredNetworkIds); 905 } 906 metered = config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED; 907 } else if (mKnownMeteredNetworkIds.contains(config.networkId)) { 908 // Use the saved information 909 metered = true; 910 } else if (metered) { 911 // Update the saved information 912 mKnownMeteredNetworkIds.add(config.networkId); 913 localLog("KnownMeteredNetworkIds = " + mKnownMeteredNetworkIds); 914 } 915 return metered; 916 } 917 918 /** 919 * Returns the set of known metered network ids (for tests. dumpsys, and metrics). 920 */ getKnownMeteredNetworkIds()921 public Set<Integer> getKnownMeteredNetworkIds() { 922 return new ArraySet<>(mKnownMeteredNetworkIds); 923 } 924 925 private final ArraySet<Integer> mKnownMeteredNetworkIds = new ArraySet<>(); 926 927 928 /** 929 * Cleans up state that should go away when wifi is disabled. 930 */ resetOnDisable()931 public void resetOnDisable() { 932 mWifiConfigManager.clearLastSelectedNetwork(); 933 mKnownMeteredNetworkIds.clear(); 934 } 935 936 /** 937 * Container class for passing the ClientModeManager state for each instance that is managed by 938 * WifiConnectivityManager, i.e all {@link ClientModeManager#getRole()} equals 939 * {@link ActiveModeManager#ROLE_CLIENT_PRIMARY} or 940 * {@link ActiveModeManager#ROLE_CLIENT_SECONDARY_LONG_LIVED}. 941 */ 942 public static class ClientModeManagerState { 943 /** Iface Name corresponding to iface (if known) */ 944 public final String ifaceName; 945 /** True if the device is connected */ 946 public final boolean connected; 947 /** True if the device is disconnected */ 948 public final boolean disconnected; 949 /** True if the device is connected in local-only mode due to ip provisioning timeout**/ 950 public final boolean ipProvisioningTimedOut; 951 /** Currently connected network */ 952 public final WifiInfo wifiInfo; 953 public final ActiveModeManager.ClientRole role; 954 ClientModeManagerState(@onNull ClientModeManager clientModeManager)955 ClientModeManagerState(@NonNull ClientModeManager clientModeManager) { 956 ifaceName = clientModeManager.getInterfaceName(); 957 connected = clientModeManager.isConnected(); 958 disconnected = clientModeManager.isDisconnected(); 959 ipProvisioningTimedOut = clientModeManager.isIpProvisioningTimedOut(); 960 wifiInfo = clientModeManager.getConnectionInfo(); 961 role = clientModeManager.getRole(); 962 } 963 ClientModeManagerState()964 ClientModeManagerState() { 965 ifaceName = "unknown"; 966 connected = false; 967 disconnected = true; 968 wifiInfo = new WifiInfo(); 969 ipProvisioningTimedOut = false; 970 role = null; 971 } 972 973 @VisibleForTesting ClientModeManagerState(@onNull String ifaceName, boolean connected, boolean disconnected, @NonNull WifiInfo wifiInfo, boolean ipProvisioningTimedOut, ActiveModeManager.ClientRole role)974 ClientModeManagerState(@NonNull String ifaceName, boolean connected, boolean disconnected, 975 @NonNull WifiInfo wifiInfo, boolean ipProvisioningTimedOut, 976 ActiveModeManager.ClientRole role) { 977 this.ifaceName = ifaceName; 978 this.connected = connected; 979 this.disconnected = disconnected; 980 this.wifiInfo = wifiInfo; 981 this.ipProvisioningTimedOut = ipProvisioningTimedOut; 982 this.role = role; 983 } 984 985 @Override equals(Object that)986 public boolean equals(Object that) { 987 if (this == that) return true; 988 if (!(that instanceof ClientModeManagerState)) return false; 989 ClientModeManagerState thatCmmState = (ClientModeManagerState) that; 990 return Objects.equals(ifaceName, thatCmmState.ifaceName) 991 && connected == thatCmmState.connected 992 && disconnected == thatCmmState.disconnected 993 && role == thatCmmState.role 994 // Since wifiinfo does not have equals currently. 995 && Objects.equals(wifiInfo.getSSID(), thatCmmState.wifiInfo.getSSID()) 996 && Objects.equals(wifiInfo.getBSSID(), thatCmmState.wifiInfo.getBSSID()); 997 } 998 999 @Override hashCode()1000 public int hashCode() { 1001 return Objects.hash(ifaceName, connected, disconnected, 1002 wifiInfo.getSSID(), wifiInfo.getBSSID(), role); 1003 } 1004 1005 @Override toString()1006 public String toString() { 1007 return "ClientModeManagerState: " + ifaceName 1008 + ", role:" + role 1009 + ", connection state: " 1010 + (connected ? " connected" : (disconnected ? " disconnected" : "unknown")) 1011 + ", WifiInfo: " + wifiInfo; 1012 } 1013 } 1014 1015 /** 1016 * Sets the screen state. 1017 */ setScreenState(boolean screenOn)1018 public void setScreenState(boolean screenOn) { 1019 mScreenOn = screenOn; 1020 } 1021 1022 /** 1023 * Sets the associated network selection override. 1024 */ setAssociatedNetworkSelectionOverride( @ssociatedNetworkSelectionOverride int value)1025 public boolean setAssociatedNetworkSelectionOverride( 1026 @AssociatedNetworkSelectionOverride int value) { 1027 if (value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE 1028 && value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED 1029 && value != ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED) { 1030 localLog("Error in setting associated network selection override. Invalid value=" 1031 + value); 1032 return false; 1033 } 1034 mAssociatedNetworkSelectionOverride = value; 1035 return true; 1036 } 1037 1038 /** 1039 * Get whether sufficiency check is enabled based on the current screen state. 1040 */ isSufficiencyCheckEnabled()1041 public boolean isSufficiencyCheckEnabled() { 1042 return mScreenOn ? mSufficiencyCheckEnabledWhenScreenOn 1043 : mSufficiencyCheckEnabledWhenScreenOff; 1044 } 1045 1046 /** 1047 * Enable or disable sufficiency check. 1048 */ setSufficiencyCheckEnabled(boolean enabledWhileScreenOff, boolean enabledWhileScreenOn)1049 public void setSufficiencyCheckEnabled(boolean enabledWhileScreenOff, 1050 boolean enabledWhileScreenOn) { 1051 mSufficiencyCheckEnabledWhenScreenOff = enabledWhileScreenOff; 1052 mSufficiencyCheckEnabledWhenScreenOn = enabledWhileScreenOn; 1053 } 1054 1055 /** 1056 * Enable or disable candidate override with user connect choice. 1057 */ setUserConnectChoiceOverrideEnabled(boolean enabled)1058 public void setUserConnectChoiceOverrideEnabled(boolean enabled) { 1059 mUserConnectChoiceOverrideEnabled = enabled; 1060 } 1061 1062 /** 1063 * Enable or disable last selection weight. 1064 */ setLastSelectionWeightEnabled(boolean enabled)1065 public void setLastSelectionWeightEnabled(boolean enabled) { 1066 mLastSelectionWeightEnabled = enabled; 1067 } 1068 getConnectChoiceKey(@onNull List<ClientModeManagerState> cmmStates)1069 private String getConnectChoiceKey(@NonNull List<ClientModeManagerState> cmmStates) { 1070 for (ClientModeManagerState cmmState : cmmStates) { 1071 if (cmmState.role != ROLE_CLIENT_PRIMARY) { 1072 continue; 1073 } 1074 WifiConfiguration currentNetwork = 1075 mWifiConfigManager.getConfiguredNetwork(cmmState.wifiInfo.getNetworkId()); 1076 if (currentNetwork != null) { 1077 return currentNetwork.getNetworkSelectionStatus().getConnectChoice(); 1078 } 1079 } 1080 return null; 1081 } 1082 1083 /** 1084 * Returns the list of Candidates from networks in range. 1085 * 1086 * @param scanDetails List of ScanDetail for all the APs in range 1087 * @param bssidBlocklist Blocked BSSIDs 1088 * @param cmmStates State of all long lived client mode manager instances - 1089 * {@link ActiveModeManager#ROLE_CLIENT_PRIMARY} & 1090 * {@link ActiveModeManager#ROLE_CLIENT_SECONDARY_LONG_LIVED}. 1091 * @param untrustedNetworkAllowed True if untrusted networks are allowed for connection 1092 * @param oemPaidNetworkAllowed True if oem paid networks are allowed for connection 1093 * @param oemPrivateNetworkAllowed True if oem private networks are allowed for connection 1094 * @param restrictedNetworkAllowedUids a set of Uids are allowed for restricted network 1095 * @param skipSufficiencyCheck True to skip network sufficiency check 1096 * @return list of valid Candidate(s) 1097 */ getCandidatesFromScan( @onNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist, @NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck, int autojoinRestrictionSecurityTypes)1098 public List<WifiCandidates.Candidate> getCandidatesFromScan( 1099 @NonNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist, 1100 @NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, 1101 boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, 1102 Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck, 1103 int autojoinRestrictionSecurityTypes) { 1104 mFilteredNetworks.clear(); 1105 mConnectableNetworks.clear(); 1106 if (scanDetails.size() == 0) { 1107 localLog("Empty connectivity scan result"); 1108 return null; 1109 } 1110 1111 // Update the scan detail cache at the start, even if we skip network selection 1112 updateScanDetailCache(scanDetails); 1113 1114 // Update the registered network nominators. 1115 for (NetworkNominator registeredNominator : mNominators) { 1116 registeredNominator.update(scanDetails); 1117 } 1118 1119 // Filter out unwanted networks. 1120 mFilteredNetworks = filterScanResults(scanDetails, bssidBlocklist, cmmStates, 1121 autojoinRestrictionSecurityTypes); 1122 if (mFilteredNetworks.size() == 0) { 1123 return null; 1124 } 1125 // Update the matching profiles into WifiConfigManager, help displaying Passpoint networks 1126 // in Wifi Picker 1127 mWifiInjector.getPasspointNetworkNominateHelper().updatePasspointConfig(mFilteredNetworks); 1128 1129 boolean networkSelectionNeeded = skipSufficiencyCheck 1130 || isNetworkSelectionNeeded(cmmStates); 1131 final String userConnectChoiceKey; 1132 if (!networkSelectionNeeded) { 1133 if (!isAssociatedNetworkSelectionEnabled()) { 1134 // Skip network selection based on connect choice because associated network 1135 // selection is disabled. 1136 return null; 1137 } 1138 userConnectChoiceKey = getConnectChoiceKey(cmmStates); 1139 if (userConnectChoiceKey == null) { 1140 return null; 1141 } 1142 // Continue candidate selection but only allow the user connect choice as candidate 1143 localLog("Current network is sufficient. Continue network selection only " 1144 + "considering user connect choice: " + userConnectChoiceKey); 1145 } else { 1146 userConnectChoiceKey = null; 1147 } 1148 1149 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext); 1150 if (userConnectChoiceKey == null) { 1151 // Add connected network as candidates unless only considering connect choice. 1152 for (ClientModeManagerState cmmState : cmmStates) { 1153 // Always get the current BSSID from WifiInfo in case that firmware initiated 1154 // roaming happened. 1155 String currentBssid = cmmState.wifiInfo.getBSSID(); 1156 WifiConfiguration currentNetwork = 1157 mWifiConfigManager.getConfiguredNetwork(cmmState.wifiInfo.getNetworkId()); 1158 if (currentNetwork != null && currentBssid != null) { 1159 wifiCandidates.setCurrent(currentNetwork.networkId, currentBssid); 1160 // We always want the current network to be a candidate so that it can 1161 // participate. 1162 // It may also get re-added by a nominator, in which case this fallback 1163 // will be replaced. 1164 MacAddress bssid = MacAddress.fromString(currentBssid); 1165 SecurityParams params = currentNetwork.getNetworkSelectionStatus() 1166 .getLastUsedSecurityParams(); 1167 if (null == params) { 1168 localLog("No known candidate security params for current network."); 1169 continue; 1170 } 1171 WifiCandidates.Key key = new WifiCandidates.Key( 1172 ScanResultMatchInfo.fromWifiConfiguration(currentNetwork), 1173 bssid, currentNetwork.networkId, 1174 params.getSecurityType()); 1175 ScanDetail scanDetail = findScanDetailForBssid(mFilteredNetworks, currentBssid); 1176 int predictedTputMbps = (scanDetail == null) ? 0 1177 : predictThroughput(scanDetail); 1178 wifiCandidates.add(key, currentNetwork, 1179 NetworkNominator.NOMINATOR_ID_CURRENT, 1180 cmmState.wifiInfo.getRssi(), 1181 cmmState.wifiInfo.getFrequency(), 1182 ScanResult.CHANNEL_WIDTH_20MHZ, // channel width unavailable in WifiInfo 1183 calculateLastSelectionWeight(currentNetwork.networkId, 1184 WifiConfiguration.isMetered(currentNetwork, cmmState.wifiInfo)), 1185 WifiConfiguration.isMetered(currentNetwork, cmmState.wifiInfo), 1186 isFromCarrierOrPrivilegedApp(currentNetwork), 1187 predictedTputMbps, 1188 (scanDetail != null) ? scanDetail.getScanResult().getApMldMacAddress() 1189 : null); 1190 } 1191 } 1192 } 1193 1194 // Update all configured networks before initiating network selection. 1195 updateConfiguredNetworks(); 1196 1197 List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates = mWifiInjector 1198 .getPasspointNetworkNominateHelper() 1199 .getPasspointNetworkCandidates(new ArrayList<>(mFilteredNetworks)); 1200 for (NetworkNominator registeredNominator : mNominators) { 1201 localLog("About to run " + registeredNominator.getName() + " :"); 1202 registeredNominator.nominateNetworks( 1203 new ArrayList<>(mFilteredNetworks), passpointCandidates, 1204 untrustedNetworkAllowed, oemPaidNetworkAllowed, oemPrivateNetworkAllowed, 1205 restrictedNetworkAllowedUids, (scanDetail, config) -> { 1206 WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig( 1207 scanDetail, config); 1208 if (key != null) { 1209 if (userConnectChoiceKey != null 1210 && !userConnectChoiceKey.equals(config.getProfileKey())) { 1211 return; 1212 } 1213 boolean metered = false; 1214 for (ClientModeManagerState cmmState : cmmStates) { 1215 if (isEverMetered(config, cmmState.wifiInfo, scanDetail)) { 1216 metered = true; 1217 break; 1218 } 1219 } 1220 // TODO(b/151981920) Saved passpoint candidates are marked ephemeral 1221 boolean added = wifiCandidates.add(key, config, 1222 registeredNominator.getId(), 1223 scanDetail.getScanResult().level, 1224 scanDetail.getScanResult().frequency, 1225 scanDetail.getScanResult().channelWidth, 1226 calculateLastSelectionWeight(config.networkId, metered), 1227 metered, 1228 isFromCarrierOrPrivilegedApp(config), 1229 predictThroughput(scanDetail), 1230 scanDetail.getScanResult().getApMldMacAddress()); 1231 if (added) { 1232 mConnectableNetworks.add(Pair.create(scanDetail, config)); 1233 mWifiConfigManager.updateScanDetailForNetwork( 1234 config.networkId, scanDetail); 1235 mWifiMetrics.setNominatorForNetwork(config.networkId, 1236 toProtoNominatorId(registeredNominator.getId())); 1237 } 1238 } 1239 }); 1240 } 1241 if (mConnectableNetworks.size() != wifiCandidates.size()) { 1242 localLog("Connectable: " + mConnectableNetworks.size() 1243 + " Candidates: " + wifiCandidates.size()); 1244 } 1245 1246 // Update multi link candidate throughput before network selection. 1247 updateMultiLinkCandidatesThroughput(wifiCandidates); 1248 1249 return wifiCandidates.getCandidates(); 1250 } 1251 1252 /** 1253 * Check Wi-Fi7 is enabled for all candidates. 1254 */ isWifi7Enabled(List<WifiCandidates.Candidate> candidates)1255 private boolean isWifi7Enabled(List<WifiCandidates.Candidate> candidates) { 1256 for (WifiCandidates.Candidate candidate : candidates) { 1257 if (!mWifiConfigManager.isWifi7Enabled(candidate.getNetworkConfigId())) return false; 1258 } 1259 return true; 1260 } 1261 1262 /** 1263 * Update multi link candidate's throughput which is used in network selection by 1264 * {@link ThroughputScorer} 1265 * 1266 * Algorithm: 1267 * {@link WifiNative#getSupportedBandCombinations(String)} returns a list of band combinations 1268 * supported by the chip. e.g. { {2.4}, {5}, {6}, {2.4, 5}, {2.4, 6}, {5, 6} }. 1269 * 1270 * During the creation of candidate list, members which have same MLD AP MAC address are grouped 1271 * together. Let's say we have the following multi link candidates in one group {C_2.4, C_5, 1272 * C_6}. First intersect this list with allowed combination to get a collection like this, 1273 * { {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, 1274 * predicted single link throughputs are added and each candidate in the subgroup get an 1275 * updated multi link throughput if the saved value is less. This calculation takes care of 1276 * eMLSR and STR. 1277 * 1278 * If the chip can't support all the radios for multi-link operation at the same time for STR 1279 * operation, we can't use the higher-order radio combinations. 1280 * 1281 * Above algorithm is extendable to multiple links with any number of bands and link 1282 * restriction. 1283 * 1284 * @param wifiCandidates A list of WifiCandidates 1285 */ updateMultiLinkCandidatesThroughput(WifiCandidates wifiCandidates)1286 private void updateMultiLinkCandidatesThroughput(WifiCandidates wifiCandidates) { 1287 ClientModeManager primaryManager = 1288 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 1289 if (primaryManager == null) return; 1290 String interfaceName = primaryManager.getInterfaceName(); 1291 if (interfaceName == null) return; 1292 1293 // Check if the chip has more than one MLO STR link support. 1294 int maxMloStrLinkCount = mWifiNative.getMaxMloStrLinkCount(interfaceName); 1295 if (maxMloStrLinkCount <= 1) return; 1296 1297 Set<List<Integer>> simultaneousBandCombinations = mWifiNative.getSupportedBandCombinations( 1298 interfaceName); 1299 if (simultaneousBandCombinations == null) return; 1300 1301 for (List<WifiCandidates.Candidate> mlCandidates : 1302 wifiCandidates.getMultiLinkCandidates()) { 1303 if (!isWifi7Enabled(mlCandidates)) continue; 1304 for (List<Integer> bands : simultaneousBandCombinations) { 1305 // Limit the radios/bands to maximum STR link supported in multi link operation. 1306 if (bands.size() > maxMloStrLinkCount) break; 1307 List<Integer> strBandsToIntersect = new ArrayList<>(bands); 1308 List<WifiCandidates.Candidate> strMlCandidates = intersectMlCandidatesWithStrBands( 1309 mlCandidates, strBandsToIntersect); 1310 if (strMlCandidates != null) { 1311 aggregateStrMultiLinkThroughput(strMlCandidates); 1312 } 1313 } 1314 } 1315 } 1316 1317 /** 1318 * Return the intersection of STR band combinations and best Multi-Link Wi-Fi candidates. 1319 */ intersectMlCandidatesWithStrBands( @onNull List<WifiCandidates.Candidate> candidates, @NonNull List<Integer> bands)1320 private List<WifiCandidates.Candidate> intersectMlCandidatesWithStrBands( 1321 @NonNull List<WifiCandidates.Candidate> candidates, @NonNull List<Integer> bands) { 1322 // Sorting is needed here to make the best candidates first in the list. 1323 List<WifiCandidates.Candidate> intersectedCandidates = candidates.stream() 1324 .sorted(Comparator.comparingInt( 1325 WifiCandidates.Candidate::getPredictedThroughputMbps).reversed()) 1326 .filter(k -> { 1327 int band = Integer.valueOf(ScanResult.toBand(k.getFrequency())); 1328 if (bands.contains(band)) { 1329 // Remove first occurrence as it is counted already. 1330 bands.remove(bands.indexOf(band)); 1331 return true; 1332 } 1333 return false; 1334 }) 1335 .collect(Collectors.toList()); 1336 // Make sure all bands are intersected. 1337 return (bands.isEmpty()) ? intersectedCandidates : null; 1338 } 1339 1340 /** 1341 * Aggregate the throughput of STR multi-link candidates. 1342 */ aggregateStrMultiLinkThroughput( @onNull List<WifiCandidates.Candidate> candidates)1343 private void aggregateStrMultiLinkThroughput( 1344 @NonNull List<WifiCandidates.Candidate> candidates) { 1345 // Add all throughputs. 1346 int predictedMlThroughput = candidates.stream() 1347 .mapToInt(c -> c.getPredictedThroughputMbps()) 1348 .sum(); 1349 // Check if an update needed for multi link throughput. 1350 candidates.stream() 1351 .filter(c -> c.getPredictedMultiLinkThroughputMbps() < predictedMlThroughput) 1352 .forEach(c -> c.setPredictedMultiLinkThroughputMbps(predictedMlThroughput)); 1353 } 1354 1355 /** 1356 * Add all results as candidates for the user selected network and let network selection 1357 * chooses the proper one for the user selected network. 1358 * @param config The configuration for the user selected network. 1359 * @param scanDetails List of ScanDetail for the user selected network. 1360 * @return list of valid Candidate(s) 1361 */ getCandidatesForUserSelection( WifiConfiguration config, @NonNull List<ScanDetail> scanDetails)1362 public List<WifiCandidates.Candidate> getCandidatesForUserSelection( 1363 WifiConfiguration config, @NonNull List<ScanDetail> scanDetails) { 1364 if (scanDetails.size() == 0) { 1365 if (mVerboseLoggingEnabled) { 1366 Log.d(TAG, "No scan result for the user selected network."); 1367 return null; 1368 } 1369 } 1370 1371 mConnectableNetworks.clear(); 1372 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext); 1373 for (ScanDetail scanDetail: scanDetails) { 1374 WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig( 1375 scanDetail, config); 1376 if (null == key) continue; 1377 1378 boolean added = wifiCandidates.add(key, config, 1379 WifiNetworkSelector.NetworkNominator.NOMINATOR_ID_CURRENT, 1380 scanDetail.getScanResult().level, 1381 scanDetail.getScanResult().frequency, 1382 scanDetail.getScanResult().channelWidth, 1383 0.0 /* lastSelectionWeightBetweenZeroAndOne */, 1384 false /* isMetered */, 1385 WifiNetworkSelector.isFromCarrierOrPrivilegedApp(config), 1386 predictThroughput(scanDetail), scanDetail.getScanResult().getApMldMacAddress()); 1387 if (!added) continue; 1388 1389 mConnectableNetworks.add(Pair.create(scanDetail, config)); 1390 mWifiConfigManager.updateScanDetailForNetwork( 1391 config.networkId, scanDetail); 1392 } 1393 return wifiCandidates.getCandidates(); 1394 } 1395 1396 /** 1397 * For transition networks with only legacy networks, 1398 * remove auto-upgrade type to use the legacy type to 1399 * avoid roaming issues between two types. 1400 */ removeAutoUpgradeSecurityParamsIfNecessary( WifiConfiguration config, List<SecurityParams> scanResultParamsList, @WifiConfiguration.SecurityType int baseSecurityType, @WifiConfiguration.SecurityType int upgradableSecurityType, boolean isLegacyNetworkInRange, boolean isUpgradableTypeOnlyInRange, boolean isAutoUpgradeEnabled)1401 private void removeAutoUpgradeSecurityParamsIfNecessary( 1402 WifiConfiguration config, 1403 List<SecurityParams> scanResultParamsList, 1404 @WifiConfiguration.SecurityType int baseSecurityType, 1405 @WifiConfiguration.SecurityType int upgradableSecurityType, 1406 boolean isLegacyNetworkInRange, 1407 boolean isUpgradableTypeOnlyInRange, 1408 boolean isAutoUpgradeEnabled) { 1409 localLog("removeAutoUpgradeSecurityParamsIfNecessary:" 1410 + " SSID: " + config.SSID 1411 + " baseSecurityType: " + baseSecurityType 1412 + " upgradableSecurityType: " + upgradableSecurityType 1413 + " isLegacyNetworkInRange: " + isLegacyNetworkInRange 1414 + " isUpgradableTypeOnlyInRange: " + isUpgradableTypeOnlyInRange 1415 + " isAutoUpgradeEnabled: " + isAutoUpgradeEnabled); 1416 if (isAutoUpgradeEnabled) { 1417 // Consider removing the auto-upgraded type if legacy networks are in range. 1418 if (!isLegacyNetworkInRange) return; 1419 // If base params is disabled or removed, keep the auto-upgrade params. 1420 SecurityParams baseParams = config.getSecurityParams(baseSecurityType); 1421 if (null == baseParams || !baseParams.isEnabled()) return; 1422 // If there are APs with standalone-upgradeable security type is in range, 1423 // do not consider removing the auto-upgraded type. 1424 if (isUpgradableTypeOnlyInRange) return; 1425 } 1426 1427 SecurityParams upgradableParams = config.getSecurityParams(upgradableSecurityType); 1428 if (null == upgradableParams) return; 1429 if (!upgradableParams.isAddedByAutoUpgrade()) return; 1430 localLog("Remove upgradable security type " + upgradableSecurityType + " for the network."); 1431 scanResultParamsList.removeIf(p -> p.isSecurityType(upgradableSecurityType)); 1432 } 1433 1434 /** Helper function to place all conditions which need to remove auto-upgrade types. */ removeSecurityParamsIfNecessary( WifiConfiguration config, List<SecurityParams> scanResultParamsList)1435 private void removeSecurityParamsIfNecessary( 1436 WifiConfiguration config, 1437 List<SecurityParams> scanResultParamsList) { 1438 // When offload is supported, both types are passed down. 1439 if (!mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()) { 1440 removeAutoUpgradeSecurityParamsIfNecessary( 1441 config, scanResultParamsList, 1442 WifiConfiguration.SECURITY_TYPE_PSK, 1443 WifiConfiguration.SECURITY_TYPE_SAE, 1444 mScanRequestProxy.isWpa2PersonalOnlyNetworkInRange(config.SSID), 1445 mScanRequestProxy.isWpa3PersonalOnlyNetworkInRange(config.SSID), 1446 mWifiGlobals.isWpa3SaeUpgradeEnabled()); 1447 } 1448 removeAutoUpgradeSecurityParamsIfNecessary( 1449 config, scanResultParamsList, 1450 WifiConfiguration.SECURITY_TYPE_OPEN, 1451 WifiConfiguration.SECURITY_TYPE_OWE, 1452 mScanRequestProxy.isOpenOnlyNetworkInRange(config.SSID), 1453 mScanRequestProxy.isOweOnlyNetworkInRange(config.SSID), 1454 mWifiGlobals.isOweUpgradeEnabled()); 1455 removeAutoUpgradeSecurityParamsIfNecessary( 1456 config, scanResultParamsList, 1457 WifiConfiguration.SECURITY_TYPE_EAP, 1458 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, 1459 mScanRequestProxy.isWpa2EnterpriseOnlyNetworkInRange(config.SSID), 1460 mScanRequestProxy.isWpa3EnterpriseOnlyNetworkInRange(config.SSID), 1461 true); 1462 // When using WPA3 (SAE), all passwords in all lengths are strings, but when using WPA2, 1463 // there is a distinction between 8-63 octets that go through BDKDF2 function, and 1464 // 64-octets that are assumed to be the output of it. BDKDF2 is not applicable to SAE 1465 // and to prevent interop issues with APs when 64-octet Hex PSK is configured, update 1466 // the configuration to use WPA2 only. 1467 WifiConfiguration configWithPassword = mWifiConfigManager 1468 .getConfiguredNetworkWithPassword(config.networkId); 1469 if (configWithPassword.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 1470 && configWithPassword.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 1471 && configWithPassword.preSharedKey != null 1472 && !configWithPassword.preSharedKey.startsWith("\"") 1473 && configWithPassword.preSharedKey.length() == 64 1474 && configWithPassword.preSharedKey.matches("[0-9A-Fa-f]{64}")) { 1475 localLog("Remove SAE type for " + configWithPassword.SSID + " with 64-octet Hex PSK."); 1476 scanResultParamsList 1477 .removeIf(p -> p.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)); 1478 } 1479 } 1480 1481 /** 1482 * For the transition mode, MFPC should be true, and MFPR should be false, 1483 * see WPA3 SAE specification section 2.3 and 3.3. 1484 */ updateSecurityParamsForTransitionModeIfNecessary( ScanResult scanResult, SecurityParams params)1485 private void updateSecurityParamsForTransitionModeIfNecessary( 1486 ScanResult scanResult, SecurityParams params) { 1487 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 1488 && params.isAddedByAutoUpgrade() 1489 && ScanResultUtil.isScanResultForPskSaeTransitionNetwork(scanResult)) { 1490 params.setRequirePmf(false); 1491 } else if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE) 1492 && params.isAddedByAutoUpgrade() 1493 && ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)) { 1494 params.setRequirePmf(false); 1495 } 1496 } 1497 1498 /** 1499 * Update the candidate security params against a scan detail. 1500 * 1501 * @param network the target network. 1502 * @param scanDetail the target scan detail. 1503 */ updateNetworkCandidateSecurityParams( WifiConfiguration network, ScanDetail scanDetail)1504 private void updateNetworkCandidateSecurityParams( 1505 WifiConfiguration network, ScanDetail scanDetail) { 1506 if (network == null) return; 1507 if (scanDetail == null) return; 1508 1509 ScanResult scanResult = scanDetail.getScanResult(); 1510 List<SecurityParams> scanResultParamsList = ScanResultUtil 1511 .generateSecurityParamsListFromScanResult(scanResult); 1512 if (scanResultParamsList == null) return; 1513 // Under some conditions, the legacy type is preferred to have better 1514 // connectivity behaviors, and the auto-upgrade type should be removed. 1515 removeSecurityParamsIfNecessary(network, scanResultParamsList); 1516 SecurityParams params = ScanResultMatchInfo.getBestMatchingSecurityParams( 1517 network, 1518 scanResultParamsList); 1519 if (params == null) return; 1520 updateSecurityParamsForTransitionModeIfNecessary(scanResult, params); 1521 mWifiConfigManager.setNetworkCandidateScanResult( 1522 network.networkId, scanResult, 0, params); 1523 } 1524 1525 /** 1526 * Using the registered Scorers, choose the best network from the list of Candidate(s). 1527 * The ScanDetailCache is also updated here. 1528 * @param candidates - Candidates to perferm network selection on. 1529 * @return WifiConfiguration - the selected network, or null. 1530 */ 1531 @Nullable selectNetwork(@onNull List<WifiCandidates.Candidate> candidates)1532 public WifiConfiguration selectNetwork(@NonNull List<WifiCandidates.Candidate> candidates) { 1533 return selectNetwork(candidates, true); 1534 } 1535 1536 /** 1537 * Using the registered Scorers, choose the best network from the list of Candidate(s). 1538 * The ScanDetailCache is also updated here. 1539 * @param candidates - Candidates to perform network selection on. 1540 * @param overrideEnabled If it is allowed to override candidate with User Connect Choice. 1541 * @return WifiConfiguration - the selected network, or null. 1542 */ 1543 @Nullable selectNetwork(@onNull List<WifiCandidates.Candidate> candidates, boolean overrideEnabled)1544 public WifiConfiguration selectNetwork(@NonNull List<WifiCandidates.Candidate> candidates, 1545 boolean overrideEnabled) { 1546 if (candidates == null || candidates.size() == 0) { 1547 return null; 1548 } 1549 WifiCandidates wifiCandidates = new WifiCandidates(mWifiScoreCard, mContext, candidates); 1550 final WifiCandidates.CandidateScorer activeScorer = getActiveCandidateScorer(); 1551 // Update the NetworkSelectionStatus in the configs for the current candidates 1552 // This is needed for the legacy user connect choice, at least 1553 Collection<Collection<WifiCandidates.Candidate>> groupedCandidates = 1554 wifiCandidates.getGroupedCandidates(); 1555 for (Collection<WifiCandidates.Candidate> group : groupedCandidates) { 1556 WifiCandidates.ScoredCandidate choice = activeScorer.scoreCandidates(group); 1557 if (choice == null) continue; 1558 ScanDetail scanDetail = getScanDetailForCandidateKey(choice.candidateKey); 1559 if (scanDetail == null) continue; 1560 WifiConfiguration config = mWifiConfigManager 1561 .getConfiguredNetwork(choice.candidateKey.networkId); 1562 if (config == null) continue; 1563 updateNetworkCandidateSecurityParams(config, scanDetail); 1564 } 1565 1566 for (Collection<WifiCandidates.Candidate> group : groupedCandidates) { 1567 for (WifiCandidates.Candidate candidate : group.stream() 1568 .sorted((a, b) -> (b.getScanRssi() - a.getScanRssi())) // decreasing rssi 1569 .collect(Collectors.toList())) { 1570 localLog(candidate.toString()); 1571 } 1572 } 1573 1574 ArrayMap<Integer, Integer> experimentNetworkSelections = new ArrayMap<>(); // for metrics 1575 1576 int selectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 1577 1578 // Run all the CandidateScorers 1579 boolean legacyOverrideWanted = true; 1580 for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) { 1581 WifiCandidates.ScoredCandidate choice; 1582 try { 1583 choice = wifiCandidates.choose(candidateScorer); 1584 } catch (RuntimeException e) { 1585 Log.wtf(TAG, "Exception running a CandidateScorer", e); 1586 continue; 1587 } 1588 int networkId = choice.candidateKey == null 1589 ? WifiConfiguration.INVALID_NETWORK_ID 1590 : choice.candidateKey.networkId; 1591 String chooses = " would choose "; 1592 if (candidateScorer == activeScorer) { 1593 chooses = " chooses "; 1594 legacyOverrideWanted = choice.userConnectChoiceOverride; 1595 selectedNetworkId = networkId; 1596 updateChosenPasspointNetwork(choice); 1597 } 1598 String id = candidateScorer.getIdentifier(); 1599 int expid = experimentIdFromIdentifier(id); 1600 localLog(id + chooses + networkId 1601 + " score " + choice.value + "+/-" + choice.err 1602 + " expid " + expid); 1603 experimentNetworkSelections.put(expid, networkId); 1604 } 1605 1606 // Update metrics about differences in the selections made by various methods 1607 final int activeExperimentId = experimentIdFromIdentifier(activeScorer.getIdentifier()); 1608 for (Map.Entry<Integer, Integer> entry : 1609 experimentNetworkSelections.entrySet()) { 1610 int experimentId = entry.getKey(); 1611 if (experimentId == activeExperimentId) continue; 1612 int thisSelectedNetworkId = entry.getValue(); 1613 mWifiMetrics.logNetworkSelectionDecision(experimentId, activeExperimentId, 1614 selectedNetworkId == thisSelectedNetworkId, 1615 groupedCandidates.size()); 1616 } 1617 1618 // Get a fresh copy of WifiConfiguration reflecting any scan result updates 1619 WifiConfiguration selectedNetwork = 1620 mWifiConfigManager.getConfiguredNetwork(selectedNetworkId); 1621 if (selectedNetwork != null && legacyOverrideWanted && overrideEnabled 1622 && mUserConnectChoiceOverrideEnabled) { 1623 selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork); 1624 } 1625 if (selectedNetwork != null) { 1626 mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); 1627 } 1628 return selectedNetwork; 1629 } 1630 1631 /** 1632 * Returns the ScanDetail given the candidate key, using the saved list of connectible networks. 1633 */ getScanDetailForCandidateKey(WifiCandidates.Key candidateKey)1634 private ScanDetail getScanDetailForCandidateKey(WifiCandidates.Key candidateKey) { 1635 if (candidateKey == null) return null; 1636 String bssid = candidateKey.bssid.toString(); 1637 for (Pair<ScanDetail, WifiConfiguration> pair : mConnectableNetworks) { 1638 if (candidateKey.networkId == pair.second.networkId 1639 && bssid.equals(pair.first.getBSSIDString())) { 1640 return pair.first; 1641 } 1642 } 1643 return null; 1644 } 1645 updateChosenPasspointNetwork(WifiCandidates.ScoredCandidate choice)1646 private void updateChosenPasspointNetwork(WifiCandidates.ScoredCandidate choice) { 1647 if (choice.candidateKey == null) { 1648 return; 1649 } 1650 WifiConfiguration config = 1651 mWifiConfigManager.getConfiguredNetwork(choice.candidateKey.networkId); 1652 if (config == null) { 1653 return; 1654 } 1655 if (config.isPasspoint()) { 1656 config.SSID = choice.candidateKey.matchInfo.networkSsid; 1657 mWifiConfigManager.addOrUpdateNetwork(config, config.creatorUid, config.creatorName, 1658 false); 1659 } 1660 } 1661 updateScanDetailCache(List<ScanDetail> scanDetails)1662 private void updateScanDetailCache(List<ScanDetail> scanDetails) { 1663 for (ScanDetail scanDetail : scanDetails) { 1664 mWifiConfigManager.updateScanDetailCacheFromScanDetailForSavedNetwork(scanDetail); 1665 } 1666 } 1667 toProtoNominatorId(@etworkNominator.NominatorId int nominatorId)1668 private static int toProtoNominatorId(@NetworkNominator.NominatorId int nominatorId) { 1669 switch (nominatorId) { 1670 case NetworkNominator.NOMINATOR_ID_SAVED: 1671 return WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED; 1672 case NetworkNominator.NOMINATOR_ID_SUGGESTION: 1673 return WifiMetricsProto.ConnectionEvent.NOMINATOR_SUGGESTION; 1674 case NetworkNominator.NOMINATOR_ID_SCORED: 1675 return WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED; 1676 case NetworkNominator.NOMINATOR_ID_CURRENT: 1677 Log.e(TAG, "Unexpected NOMINATOR_ID_CURRENT", new RuntimeException()); 1678 return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN; 1679 default: 1680 Log.e(TAG, "UnrecognizedNominatorId" + nominatorId); 1681 return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN; 1682 } 1683 } 1684 calculateLastSelectionWeight(int networkId, boolean isMetered)1685 private double calculateLastSelectionWeight(int networkId, boolean isMetered) { 1686 if (!mLastSelectionWeightEnabled 1687 || networkId != mWifiConfigManager.getLastSelectedNetwork()) { 1688 return 0.0; 1689 } 1690 double timeDifference = mClock.getElapsedSinceBootMillis() 1691 - mWifiConfigManager.getLastSelectedTimeStamp(); 1692 long millis = TimeUnit.MINUTES.toMillis(isMetered 1693 ? mScoringParams.getLastMeteredSelectionMinutes() 1694 : mScoringParams.getLastUnmeteredSelectionMinutes()); 1695 if (timeDifference >= millis) return 0.0; 1696 double unclipped = 1.0 - (timeDifference / millis); 1697 return Math.min(Math.max(unclipped, 0.0), 1.0); 1698 } 1699 getActiveCandidateScorer()1700 private WifiCandidates.CandidateScorer getActiveCandidateScorer() { 1701 WifiCandidates.CandidateScorer ans = mCandidateScorers.get(PRESET_CANDIDATE_SCORER_NAME); 1702 int overrideExperimentId = mScoringParams.getExperimentIdentifier(); 1703 if (overrideExperimentId >= MIN_SCORER_EXP_ID) { 1704 for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) { 1705 int expId = experimentIdFromIdentifier(candidateScorer.getIdentifier()); 1706 if (expId == overrideExperimentId) { 1707 ans = candidateScorer; 1708 break; 1709 } 1710 } 1711 } 1712 if (ans == null && PRESET_CANDIDATE_SCORER_NAME != null) { 1713 Log.wtf(TAG, PRESET_CANDIDATE_SCORER_NAME + " is not registered!"); 1714 } 1715 mWifiMetrics.setNetworkSelectorExperimentId(ans == null 1716 ? LEGACY_CANDIDATE_SCORER_EXP_ID 1717 : experimentIdFromIdentifier(ans.getIdentifier())); 1718 return ans; 1719 } 1720 predictThroughput(@onNull ScanDetail scanDetail)1721 private int predictThroughput(@NonNull ScanDetail scanDetail) { 1722 if (scanDetail.getScanResult() == null || scanDetail.getNetworkDetail() == null) { 1723 return 0; 1724 } 1725 int channelUtilizationLinkLayerStats = BssLoad.INVALID; 1726 if (mWifiChannelUtilization != null) { 1727 channelUtilizationLinkLayerStats = 1728 mWifiChannelUtilization.getUtilizationRatio( 1729 scanDetail.getScanResult().frequency); 1730 } 1731 ClientModeManager primaryManager = 1732 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); 1733 return mThroughputPredictor.predictThroughput( 1734 primaryManager.getDeviceWiphyCapabilities(), 1735 scanDetail.getScanResult().getWifiStandard(), 1736 scanDetail.getScanResult().channelWidth, 1737 scanDetail.getScanResult().level, 1738 scanDetail.getScanResult().frequency, 1739 scanDetail.getNetworkDetail().getMaxNumberSpatialStreams(), 1740 scanDetail.getNetworkDetail().getChannelUtilization(), 1741 channelUtilizationLinkLayerStats, 1742 mWifiGlobals.isBluetoothConnected(), 1743 scanDetail.getNetworkDetail().getDisabledSubchannelBitmap()); 1744 } 1745 1746 /** 1747 * Register a network nominator 1748 * 1749 * @param nominator the network nominator to be registered 1750 */ registerNetworkNominator(@onNull NetworkNominator nominator)1751 public void registerNetworkNominator(@NonNull NetworkNominator nominator) { 1752 mNominators.add(Preconditions.checkNotNull(nominator)); 1753 } 1754 1755 /** 1756 * Register a candidate scorer. 1757 * 1758 * Replaces any existing scorer having the same identifier. 1759 */ registerCandidateScorer(@onNull WifiCandidates.CandidateScorer candidateScorer)1760 public void registerCandidateScorer(@NonNull WifiCandidates.CandidateScorer candidateScorer) { 1761 String name = Preconditions.checkNotNull(candidateScorer).getIdentifier(); 1762 if (name != null) { 1763 mCandidateScorers.put(name, candidateScorer); 1764 } 1765 } 1766 1767 /** 1768 * Unregister a candidate scorer. 1769 */ unregisterCandidateScorer(@onNull WifiCandidates.CandidateScorer candidateScorer)1770 public void unregisterCandidateScorer(@NonNull WifiCandidates.CandidateScorer candidateScorer) { 1771 String name = Preconditions.checkNotNull(candidateScorer).getIdentifier(); 1772 if (name != null) { 1773 mCandidateScorers.remove(name); 1774 } 1775 } 1776 1777 /** 1778 * Indicate whether or not a configuration is from carrier or privileged app. 1779 * 1780 * @param config The network configuration 1781 * @return true if this configuration is from carrier or privileged app; false otherwise. 1782 */ isFromCarrierOrPrivilegedApp(WifiConfiguration config)1783 public static boolean isFromCarrierOrPrivilegedApp(WifiConfiguration config) { 1784 if (config.fromWifiNetworkSuggestion 1785 && config.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 1786 // Privileged carrier suggestion 1787 return true; 1788 } 1789 if (config.isEphemeral() 1790 && !config.fromWifiNetworkSpecifier 1791 && !config.fromWifiNetworkSuggestion) { 1792 // From ScoredNetworkNominator 1793 return true; 1794 } 1795 return false; 1796 } 1797 1798 /** 1799 * Derives a numeric experiment identifier from a CandidateScorer's identifier. 1800 * 1801 * @returns a positive number that starts with the decimal digits ID_PREFIX 1802 */ experimentIdFromIdentifier(String id)1803 public static int experimentIdFromIdentifier(String id) { 1804 final int digits = (int) (((long) id.hashCode()) & Integer.MAX_VALUE) % ID_SUFFIX_MOD; 1805 return ID_PREFIX * ID_SUFFIX_MOD + digits; 1806 } 1807 1808 private static final int ID_SUFFIX_MOD = 1_000_000; 1809 private static final int ID_PREFIX = 42; 1810 private static final int MIN_SCORER_EXP_ID = ID_PREFIX * ID_SUFFIX_MOD; 1811 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)1812 WifiNetworkSelector( 1813 Context context, 1814 WifiScoreCard wifiScoreCard, 1815 ScoringParams scoringParams, 1816 WifiConfigManager configManager, 1817 Clock clock, 1818 LocalLog localLog, 1819 WifiMetrics wifiMetrics, 1820 WifiInjector wifiInjector, 1821 ThroughputPredictor throughputPredictor, 1822 WifiChannelUtilization wifiChannelUtilization, 1823 WifiGlobals wifiGlobals, 1824 ScanRequestProxy scanRequestProxy, 1825 WifiNative wifiNative) { 1826 mContext = context; 1827 mWifiScoreCard = wifiScoreCard; 1828 mScoringParams = scoringParams; 1829 mWifiConfigManager = configManager; 1830 mClock = clock; 1831 mLocalLog = localLog; 1832 mWifiMetrics = wifiMetrics; 1833 mWifiInjector = wifiInjector; 1834 mThroughputPredictor = throughputPredictor; 1835 mWifiChannelUtilization = wifiChannelUtilization; 1836 mWifiGlobals = wifiGlobals; 1837 mScanRequestProxy = scanRequestProxy; 1838 mWifiNative = wifiNative; 1839 mDevicePolicyManager = WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext); 1840 } 1841 } 1842