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