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 com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.net.Network; 24 import android.net.NetworkCapabilities; 25 import android.net.NetworkScore; 26 import android.net.wifi.IWifiConnectedNetworkScorer; 27 import android.net.wifi.WifiConfiguration; 28 import android.net.wifi.WifiConnectedSessionInfo; 29 import android.net.wifi.WifiInfo; 30 import android.net.wifi.WifiManager; 31 import android.net.wifi.nl80211.WifiNl80211Manager; 32 import android.os.Build; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 import android.util.Log; 36 37 import androidx.annotation.RequiresApi; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.modules.utils.build.SdkLevel; 41 import com.android.server.wifi.ActiveModeManager.ClientRole; 42 import com.android.wifi.resources.R; 43 44 import java.io.FileDescriptor; 45 import java.io.PrintWriter; 46 import java.util.Calendar; 47 import java.util.LinkedList; 48 49 /** 50 * Class used to calculate scores for connected wifi networks and report it to the associated 51 * network agent. 52 */ 53 public class WifiScoreReport { 54 private static final String TAG = "WifiScoreReport"; 55 56 private static final int DUMPSYS_ENTRY_COUNT_LIMIT = 3600; // 3 hours on 3 second poll 57 58 private boolean mVerboseLoggingEnabled = false; 59 private static final long FIRST_REASONABLE_WALL_CLOCK = 1490000000000L; // mid-December 2016 60 61 private static final long MIN_TIME_TO_KEEP_BELOW_TRANSITION_SCORE_MILLIS = 9000; 62 private long mLastDownwardBreachTimeMillis = 0; 63 64 private static final int WIFI_CONNECTED_NETWORK_SCORER_IDENTIFIER = 0; 65 private static final int INVALID_SESSION_ID = -1; 66 private static final long MIN_TIME_TO_WAIT_BEFORE_BLOCKLIST_BSSID_MILLIS = 29000; 67 private static final long INVALID_WALL_CLOCK_MILLIS = -1; 68 private static final int WIFI_SCORE_TO_TERMINATE_CONNECTION_BLOCKLIST_BSSID = -2; 69 70 /** 71 * Set lingering score to be artificially lower than all other scores so that it will force 72 * ConnectivityService to prefer any other network over this one. 73 */ 74 @VisibleForTesting 75 static final int LINGERING_SCORE = 1; 76 77 // Cache of the last score 78 private int mLegacyIntScore = ConnectedScore.WIFI_INITIAL_SCORE; 79 // Cache of the last usability status 80 private boolean mIsUsable = true; 81 82 /** 83 * If true, indicates that the associated {@link ClientModeImpl} instance is lingering 84 * as a part of make before break STA + STA use-case, and will always send 85 * {@link #LINGERING_SCORE} to NetworkAgent. 86 */ 87 private boolean mShouldReduceNetworkScore = false; 88 89 /** The current role of the ClientModeManager which owns this instance of WifiScoreReport. */ 90 @Nullable 91 private ClientRole mCurrentRole = null; 92 93 private final ScoringParams mScoringParams; 94 private final Clock mClock; 95 private int mSessionNumber = 0; // not to be confused with sessionid, this just counts resets 96 private final String mInterfaceName; 97 private final WifiBlocklistMonitor mWifiBlocklistMonitor; 98 private final WifiScoreCard mWifiScoreCard; 99 private final Context mContext; 100 private long mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 101 private long mLastScoreBreachHighTimeMillis = INVALID_WALL_CLOCK_MILLIS; 102 103 private final ConnectedScore mAggressiveConnectedScore; 104 private VelocityBasedConnectedScore mVelocityBasedConnectedScore; 105 private final WifiSettingsStore mWifiSettingsStore; 106 private int mSessionIdNoReset = INVALID_SESSION_ID; 107 // Indicate whether current network is selected by the user 108 private boolean mIsUserSelected = false; 109 110 @Nullable 111 private WifiNetworkAgent mNetworkAgent; 112 private final WifiMetrics mWifiMetrics; 113 private final ExtendedWifiInfo mWifiInfo; 114 private final WifiNative mWifiNative; 115 private final WifiThreadRunner mWifiThreadRunner; 116 private final DeviceConfigFacade mDeviceConfigFacade; 117 private final ExternalScoreUpdateObserverProxy mExternalScoreUpdateObserverProxy; 118 private final WifiInfo mWifiInfoNoReset; 119 private final WifiGlobals mWifiGlobals; 120 private final ActiveModeWarden mActiveModeWarden; 121 private final WifiConnectivityManager mWifiConnectivityManager; 122 private final WifiConfigManager mWifiConfigManager; 123 private long mLastLowScoreScanTimestampMs = -1; 124 private WifiConfiguration mCurrentWifiConfiguration; 125 126 /** 127 * Callback from {@link ExternalScoreUpdateObserverProxy} 128 */ 129 private class ScoreUpdateObserverProxy implements WifiManager.ScoreUpdateObserver { 130 @Override notifyScoreUpdate(int sessionId, int score)131 public void notifyScoreUpdate(int sessionId, int score) { 132 if (mWifiConnectedNetworkScorerHolder == null 133 || sessionId == INVALID_SESSION_ID 134 || sessionId != getCurrentSessionId()) { 135 Log.w(TAG, "Ignoring stale/invalid external score" 136 + " sessionId=" + sessionId 137 + " currentSessionId=" + getCurrentSessionId() 138 + " score=" + score); 139 return; 140 } 141 long millis = mClock.getWallClockMillis(); 142 if (SdkLevel.isAtLeastS()) { 143 mLegacyIntScore = score; 144 // Only primary network can have external scorer. 145 updateWifiMetrics(millis, -1); 146 return; 147 } 148 // TODO (b/207058915): Disconnect WiFi and blocklist current BSSID. This is to 149 // maintain backward compatibility for WiFi mainline module on Android 11, and can be 150 // removed when the WiFi mainline module is no longer updated on Android 11. 151 if (score == WIFI_SCORE_TO_TERMINATE_CONNECTION_BLOCKLIST_BSSID) { 152 mWifiBlocklistMonitor.handleBssidConnectionFailure(mWifiInfoNoReset.getBSSID(), 153 mCurrentWifiConfiguration, 154 WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_CONNECTED_SCORE, 155 mWifiInfoNoReset.getRssi()); 156 return; 157 } 158 if (score > ConnectedScore.WIFI_MAX_SCORE 159 || score < ConnectedScore.WIFI_MIN_SCORE) { 160 Log.e(TAG, "Invalid score value from external scorer: " + score); 161 return; 162 } 163 if (score < ConnectedScore.WIFI_TRANSITION_SCORE) { 164 if (mLegacyIntScore >= ConnectedScore.WIFI_TRANSITION_SCORE) { 165 mLastScoreBreachLowTimeMillis = millis; 166 } 167 } else { 168 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 169 } 170 if (score > ConnectedScore.WIFI_TRANSITION_SCORE) { 171 if (mLegacyIntScore <= ConnectedScore.WIFI_TRANSITION_SCORE) { 172 mLastScoreBreachHighTimeMillis = millis; 173 } 174 } else { 175 mLastScoreBreachHighTimeMillis = INVALID_WALL_CLOCK_MILLIS; 176 } 177 mLegacyIntScore = score; 178 reportNetworkScoreToConnectivityServiceIfNecessary(); 179 updateWifiMetrics(millis, -1); 180 } 181 182 @Override triggerUpdateOfWifiUsabilityStats(int sessionId)183 public void triggerUpdateOfWifiUsabilityStats(int sessionId) { 184 if (mWifiConnectedNetworkScorerHolder == null 185 || sessionId == INVALID_SESSION_ID 186 || sessionId != getCurrentSessionId() 187 || mInterfaceName == null) { 188 Log.w(TAG, "Ignoring triggerUpdateOfWifiUsabilityStats" 189 + " sessionId=" + sessionId 190 + " currentSessionId=" + getCurrentSessionId() 191 + " interfaceName=" + mInterfaceName); 192 return; 193 } 194 WifiLinkLayerStats stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName); 195 196 // update mWifiInfo 197 // TODO(b/153075963): Better coordinate this class and ClientModeImpl to remove 198 // redundant codes below and in ClientModeImpl#fetchRssiLinkSpeedAndFrequencyNative. 199 WifiNl80211Manager.SignalPollResult pollResult = 200 mWifiNative.signalPoll(mInterfaceName); 201 if (pollResult != null) { 202 int newRssi = pollResult.currentRssiDbm; 203 int newTxLinkSpeed = pollResult.txBitrateMbps; 204 int newFrequency = pollResult.associationFrequencyMHz; 205 int newRxLinkSpeed = pollResult.rxBitrateMbps; 206 207 if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) { 208 if (newRssi > (WifiInfo.INVALID_RSSI + 256)) { 209 Log.wtf(TAG, "Error! +ve value RSSI: " + newRssi); 210 newRssi -= 256; 211 } 212 mWifiInfo.setRssi(newRssi); 213 } else { 214 mWifiInfo.setRssi(WifiInfo.INVALID_RSSI); 215 } 216 /* 217 * set Tx link speed only if it is valid 218 */ 219 if (newTxLinkSpeed > 0) { 220 mWifiInfo.setLinkSpeed(newTxLinkSpeed); 221 mWifiInfo.setTxLinkSpeedMbps(newTxLinkSpeed); 222 } 223 /* 224 * set Rx link speed only if it is valid 225 */ 226 if (newRxLinkSpeed > 0) { 227 mWifiInfo.setRxLinkSpeedMbps(newRxLinkSpeed); 228 } 229 if (newFrequency > 0) { 230 mWifiInfo.setFrequency(newFrequency); 231 } 232 } 233 234 // TODO(b/153075963): This should not be plumbed through WifiMetrics 235 mWifiMetrics.updateWifiUsabilityStatsEntries(mInterfaceName, mWifiInfo, stats); 236 } 237 238 @Override notifyStatusUpdate(int sessionId, boolean isUsable)239 public void notifyStatusUpdate(int sessionId, boolean isUsable) { 240 if (mWifiConnectedNetworkScorerHolder == null 241 || sessionId == INVALID_SESSION_ID 242 || sessionId != getCurrentSessionId()) { 243 Log.w(TAG, "Ignoring stale/invalid external status" 244 + " sessionId=" + sessionId 245 + " currentSessionId=" + getCurrentSessionId() 246 + " isUsable=" + isUsable); 247 return; 248 } 249 if (mNetworkAgent == null) { 250 return; 251 } 252 if (mShouldReduceNetworkScore) { 253 return; 254 } 255 mIsUsable = isUsable; 256 // Wifi is set to be usable if adaptive connectivity is disabled. 257 if (!mAdaptiveConnectivityEnabledSettingObserver.get() 258 || !mWifiSettingsStore.isWifiScoringEnabled()) { 259 mIsUsable = true; 260 if (mVerboseLoggingEnabled) { 261 Log.d(TAG, "Wifi scoring disabled - Notify that Wifi is usable"); 262 } 263 } 264 // Send `exiting` to NetworkScore, but don't update and send mLegacyIntScore 265 // and don't change any other fields. All we want to do is relay to ConnectivityService 266 // whether the current network is usable. 267 if (SdkLevel.isAtLeastS()) { 268 mNetworkAgent.sendNetworkScore( 269 getScoreBuilder() 270 .setLegacyInt(mLegacyIntScore) 271 .setExiting(!mIsUsable) 272 .build()); 273 } else { 274 mNetworkAgent.sendNetworkScore(mIsUsable ? ConnectedScore.WIFI_TRANSITION_SCORE + 1 275 : ConnectedScore.WIFI_TRANSITION_SCORE - 1); 276 } 277 mWifiInfo.setUsable(mIsUsable); 278 } 279 280 @Override requestNudOperation(int sessionId)281 public void requestNudOperation(int sessionId) { 282 if (mWifiConnectedNetworkScorerHolder == null 283 || sessionId == INVALID_SESSION_ID 284 || sessionId != getCurrentSessionId()) { 285 Log.w(TAG, "Ignoring stale/invalid external input for NUD triggering" 286 + " sessionId=" + sessionId 287 + " currentSessionId=" + getCurrentSessionId()); 288 return; 289 } 290 if (!mAdaptiveConnectivityEnabledSettingObserver.get() 291 || !mWifiSettingsStore.isWifiScoringEnabled()) { 292 if (mVerboseLoggingEnabled) { 293 Log.d(TAG, "Wifi scoring disabled - Cannot request a NUD operation"); 294 } 295 return; 296 } 297 mWifiConnectedNetworkScorerHolder.setShouldCheckIpLayerOnce(true); 298 } 299 300 @Override blocklistCurrentBssid(int sessionId)301 public void blocklistCurrentBssid(int sessionId) { 302 if (mWifiConnectedNetworkScorerHolder == null 303 || sessionId == INVALID_SESSION_ID 304 || sessionId != mSessionIdNoReset) { 305 Log.w(TAG, "Ignoring stale/invalid external input for blocklisting" 306 + " sessionId=" + sessionId 307 + " mSessionIdNoReset=" + mSessionIdNoReset); 308 return; 309 } 310 if (!mAdaptiveConnectivityEnabledSettingObserver.get() 311 || !mWifiSettingsStore.isWifiScoringEnabled()) { 312 if (mVerboseLoggingEnabled) { 313 Log.d(TAG, "Wifi scoring disabled - Cannot blocklist current BSSID"); 314 } 315 return; 316 } 317 if (mWifiInfoNoReset.getBSSID() != null) { 318 mWifiBlocklistMonitor.handleBssidConnectionFailure(mWifiInfoNoReset.getBSSID(), 319 mCurrentWifiConfiguration, 320 WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_CONNECTED_SCORE, 321 mWifiInfoNoReset.getRssi()); 322 } 323 } 324 } 325 326 /** 327 * If true, put the WifiScoreReport in lingering mode. A very low score is reported to 328 * NetworkAgent, and the real score is never reported. 329 */ setShouldReduceNetworkScore(boolean shouldReduceNetworkScore)330 public void setShouldReduceNetworkScore(boolean shouldReduceNetworkScore) { 331 Log.d(TAG, "setShouldReduceNetworkScore=" + shouldReduceNetworkScore 332 + " mNetworkAgent is null? " + (mNetworkAgent == null)); 333 mShouldReduceNetworkScore = shouldReduceNetworkScore; 334 // inform the external scorer that ongoing session has ended (since the score is no longer 335 // under their control) 336 if (mShouldReduceNetworkScore && mWifiConnectedNetworkScorerHolder != null) { 337 mWifiConnectedNetworkScorerHolder.stopSession(); 338 } 339 // if set to true, send score below disconnect threshold to start lingering 340 sendNetworkScore(); 341 } 342 343 /** 344 * Report network score to connectivity service. 345 */ reportNetworkScoreToConnectivityServiceIfNecessary()346 private void reportNetworkScoreToConnectivityServiceIfNecessary() { 347 if (mNetworkAgent == null) { 348 return; 349 } 350 if (mWifiConnectedNetworkScorerHolder == null && mLegacyIntScore == mWifiInfo.getScore()) { 351 return; 352 } 353 // only send network score if not lingering. If lingering, would have already sent score at 354 // start of lingering. 355 if (mShouldReduceNetworkScore) { 356 return; 357 } 358 if (mWifiConnectedNetworkScorerHolder != null 359 && mContext.getResources().getBoolean( 360 R.bool.config_wifiMinConfirmationDurationSendNetworkScoreEnabled) 361 /// Turn off hysteresis/dampening for shell commands. 362 && !mWifiConnectedNetworkScorerHolder.isShellCommandScorer()) { 363 long millis = mClock.getWallClockMillis(); 364 if (mLastScoreBreachLowTimeMillis != INVALID_WALL_CLOCK_MILLIS) { 365 if (mWifiInfo.getRssi() 366 >= mDeviceConfigFacade.getRssiThresholdNotSendLowScoreToCsDbm()) { 367 Log.d(TAG, "Not reporting low score because RSSI is high " 368 + mWifiInfo.getRssi()); 369 return; 370 } 371 if ((millis - mLastScoreBreachLowTimeMillis) 372 < mDeviceConfigFacade.getMinConfirmationDurationSendLowScoreMs()) { 373 Log.d(TAG, "Not reporting low score because elapsed time is shorter than " 374 + "the minimum confirmation duration"); 375 return; 376 } 377 } 378 if (mLastScoreBreachHighTimeMillis != INVALID_WALL_CLOCK_MILLIS 379 && (millis - mLastScoreBreachHighTimeMillis) 380 < mDeviceConfigFacade.getMinConfirmationDurationSendHighScoreMs()) { 381 Log.d(TAG, "Not reporting high score because elapsed time is shorter than " 382 + "the minimum confirmation duration"); 383 return; 384 } 385 } 386 // Stay a notch above the transition score if adaptive connectivity is disabled. 387 if (!mAdaptiveConnectivityEnabledSettingObserver.get() 388 || !mWifiSettingsStore.isWifiScoringEnabled()) { 389 mLegacyIntScore = ConnectedScore.WIFI_TRANSITION_SCORE + 1; 390 if (mVerboseLoggingEnabled) { 391 Log.d(TAG, "Wifi scoring disabled - Stay a notch above the transition score"); 392 } 393 } 394 sendNetworkScore(); 395 } 396 397 /** 398 * Container for storing info about external scorer and tracking its death. 399 */ 400 private final class WifiConnectedNetworkScorerHolder implements IBinder.DeathRecipient { 401 private final IBinder mBinder; 402 private final IWifiConnectedNetworkScorer mScorer; 403 private int mSessionId = INVALID_SESSION_ID; 404 private boolean mShouldCheckIpLayerOnce = false; 405 WifiConnectedNetworkScorerHolder(IBinder binder, IWifiConnectedNetworkScorer scorer)406 WifiConnectedNetworkScorerHolder(IBinder binder, IWifiConnectedNetworkScorer scorer) { 407 mBinder = binder; 408 mScorer = scorer; 409 } 410 411 /** 412 * Link WiFi connected scorer to death listener. 413 */ linkScorerToDeath()414 public boolean linkScorerToDeath() { 415 try { 416 mBinder.linkToDeath(this, 0); 417 } catch (RemoteException e) { 418 Log.e(TAG, "Unable to linkToDeath Wifi connected network scorer " + mScorer, e); 419 return false; 420 } 421 return true; 422 } 423 424 /** 425 * App hosting the binder has died. 426 */ 427 @Override binderDied()428 public void binderDied() { 429 mWifiThreadRunner.post(() -> revertToDefaultConnectedScorer()); 430 } 431 432 /** 433 * Unlink this object from binder death. 434 */ reset()435 public void reset() { 436 mBinder.unlinkToDeath(this, 0); 437 } 438 439 /** 440 * Starts a new scoring session. 441 */ startSession(int sessionId, boolean isUserSelected)442 public void startSession(int sessionId, boolean isUserSelected) { 443 if (sessionId == INVALID_SESSION_ID) { 444 throw new IllegalArgumentException(); 445 } 446 if (mSessionId != INVALID_SESSION_ID) { 447 // This is not expected to happen, log if it does 448 Log.e(TAG, "Stopping session " + mSessionId + " before starting " + sessionId); 449 stopSession(); 450 } 451 // Bail now if the scorer has gone away 452 if (this != mWifiConnectedNetworkScorerHolder) { 453 return; 454 } 455 mSessionId = sessionId; 456 mSessionIdNoReset = sessionId; 457 try { 458 WifiConnectedSessionInfo sessionInfo = 459 new WifiConnectedSessionInfo.Builder(sessionId) 460 .setUserSelected(isUserSelected) 461 .build(); 462 mScorer.onStart(sessionInfo); 463 } catch (RemoteException e) { 464 Log.e(TAG, "Unable to start Wifi connected network scorer " + this, e); 465 revertToDefaultConnectedScorer(); 466 } 467 } stopSession()468 public void stopSession() { 469 final int sessionId = mSessionId; 470 if (sessionId == INVALID_SESSION_ID) return; 471 mSessionId = INVALID_SESSION_ID; 472 mShouldCheckIpLayerOnce = false; 473 try { 474 mScorer.onStop(sessionId); 475 } catch (RemoteException e) { 476 Log.e(TAG, "Unable to stop Wifi connected network scorer " + this, e); 477 revertToDefaultConnectedScorer(); 478 } 479 } 480 isShellCommandScorer()481 public boolean isShellCommandScorer() { 482 return mScorer instanceof WifiShellCommand.WifiScorer; 483 } 484 setShouldCheckIpLayerOnce(boolean shouldCheckIpLayerOnce)485 private void setShouldCheckIpLayerOnce(boolean shouldCheckIpLayerOnce) { 486 mShouldCheckIpLayerOnce = shouldCheckIpLayerOnce; 487 } 488 getShouldCheckIpLayerOnce()489 private boolean getShouldCheckIpLayerOnce() { 490 return mShouldCheckIpLayerOnce; 491 } 492 } 493 494 private final ScoreUpdateObserverProxy mScoreUpdateObserverCallback = 495 new ScoreUpdateObserverProxy(); 496 497 @Nullable 498 private WifiConnectedNetworkScorerHolder mWifiConnectedNetworkScorerHolder; 499 500 private final AdaptiveConnectivityEnabledSettingObserver 501 mAdaptiveConnectivityEnabledSettingObserver; 502 WifiScoreReport(ScoringParams scoringParams, Clock clock, WifiMetrics wifiMetrics, ExtendedWifiInfo wifiInfo, WifiNative wifiNative, WifiBlocklistMonitor wifiBlocklistMonitor, WifiThreadRunner wifiThreadRunner, WifiScoreCard wifiScoreCard, DeviceConfigFacade deviceConfigFacade, Context context, AdaptiveConnectivityEnabledSettingObserver adaptiveConnectivityEnabledSettingObserver, String interfaceName, ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy, WifiSettingsStore wifiSettingsStore, WifiGlobals wifiGlobals, ActiveModeWarden activeModeWarden, WifiConnectivityManager wifiConnectivityManager, WifiConfigManager wifiConfigManager)503 WifiScoreReport(ScoringParams scoringParams, Clock clock, WifiMetrics wifiMetrics, 504 ExtendedWifiInfo wifiInfo, WifiNative wifiNative, 505 WifiBlocklistMonitor wifiBlocklistMonitor, 506 WifiThreadRunner wifiThreadRunner, WifiScoreCard wifiScoreCard, 507 DeviceConfigFacade deviceConfigFacade, Context context, 508 AdaptiveConnectivityEnabledSettingObserver adaptiveConnectivityEnabledSettingObserver, 509 String interfaceName, 510 ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy, 511 WifiSettingsStore wifiSettingsStore, 512 WifiGlobals wifiGlobals, 513 ActiveModeWarden activeModeWarden, 514 WifiConnectivityManager wifiConnectivityManager, 515 WifiConfigManager wifiConfigManager) { 516 mScoringParams = scoringParams; 517 mClock = clock; 518 mAdaptiveConnectivityEnabledSettingObserver = adaptiveConnectivityEnabledSettingObserver; 519 mAggressiveConnectedScore = new AggressiveConnectedScore(scoringParams, clock); 520 mVelocityBasedConnectedScore = new VelocityBasedConnectedScore(scoringParams, clock); 521 mWifiMetrics = wifiMetrics; 522 mWifiInfo = wifiInfo; 523 mWifiNative = wifiNative; 524 mWifiBlocklistMonitor = wifiBlocklistMonitor; 525 mWifiThreadRunner = wifiThreadRunner; 526 mWifiScoreCard = wifiScoreCard; 527 mDeviceConfigFacade = deviceConfigFacade; 528 mContext = context; 529 mInterfaceName = interfaceName; 530 mExternalScoreUpdateObserverProxy = externalScoreUpdateObserverProxy; 531 mWifiSettingsStore = wifiSettingsStore; 532 mWifiInfoNoReset = new WifiInfo(mWifiInfo); 533 mWifiGlobals = wifiGlobals; 534 mActiveModeWarden = activeModeWarden; 535 mWifiConnectivityManager = wifiConnectivityManager; 536 mWifiConfigManager = wifiConfigManager; 537 } 538 539 /** Returns whether this scores primary network based on the role */ isPrimary()540 private boolean isPrimary() { 541 return mCurrentRole != null && mCurrentRole == ActiveModeManager.ROLE_CLIENT_PRIMARY; 542 } 543 544 /** 545 * Reset the last calculated score. 546 */ reset()547 public void reset() { 548 mSessionNumber++; 549 mLegacyIntScore = isPrimary() ? ConnectedScore.WIFI_INITIAL_SCORE 550 : ConnectedScore.WIFI_SECONDARY_INITIAL_SCORE; 551 mIsUsable = true; 552 mLastKnownNudCheckScore = isPrimary() ? ConnectedScore.WIFI_TRANSITION_SCORE 553 : ConnectedScore.WIFI_SECONDARY_TRANSITION_SCORE; 554 mAggressiveConnectedScore.reset(); 555 if (mVelocityBasedConnectedScore != null) { 556 mVelocityBasedConnectedScore.reset(); 557 } 558 mLastDownwardBreachTimeMillis = 0; 559 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 560 mLastScoreBreachHighTimeMillis = INVALID_WALL_CLOCK_MILLIS; 561 mLastLowScoreScanTimestampMs = -1; 562 if (mVerboseLoggingEnabled) Log.d(TAG, "reset"); 563 } 564 565 /** 566 * Enable/Disable verbose logging in score report generation. 567 */ enableVerboseLogging(boolean enable)568 public void enableVerboseLogging(boolean enable) { 569 mVerboseLoggingEnabled = enable; 570 } 571 572 /** 573 * Calculate wifi network score based on updated link layer stats and send the score to 574 * the WifiNetworkAgent. 575 * 576 * If the score has changed from the previous value, update the WifiNetworkAgent. 577 * 578 * Called periodically (POLL_RSSI_INTERVAL_MSECS) about every 3 seconds. 579 */ calculateAndReportScore()580 public void calculateAndReportScore() { 581 // Bypass AOSP scorer if Wifi connected network scorer is set 582 if (mWifiConnectedNetworkScorerHolder != null) { 583 return; 584 } 585 586 if (mWifiInfo.getRssi() == mWifiInfo.INVALID_RSSI) { 587 Log.d(TAG, "Not reporting score because RSSI is invalid"); 588 return; 589 } 590 int score; 591 592 long millis = mClock.getWallClockMillis(); 593 mVelocityBasedConnectedScore.updateUsingWifiInfo(mWifiInfo, millis); 594 595 int s2 = mVelocityBasedConnectedScore.generateScore(); 596 score = s2; 597 598 final int transitionScore = isPrimary() ? ConnectedScore.WIFI_TRANSITION_SCORE 599 : ConnectedScore.WIFI_SECONDARY_TRANSITION_SCORE; 600 final int maxScore = isPrimary() ? ConnectedScore.WIFI_MAX_SCORE 601 : ConnectedScore.WIFI_MAX_SCORE - ConnectedScore.WIFI_SECONDARY_DELTA_SCORE; 602 603 if (mWifiInfo.getScore() > transitionScore && score <= transitionScore 604 && mWifiInfo.getSuccessfulTxPacketsPerSecond() 605 >= mScoringParams.getYippeeSkippyPacketsPerSecond() 606 && mWifiInfo.getSuccessfulRxPacketsPerSecond() 607 >= mScoringParams.getYippeeSkippyPacketsPerSecond() 608 ) { 609 score = transitionScore + 1; 610 } 611 612 if (mWifiInfo.getScore() > transitionScore && score <= transitionScore) { 613 // We don't want to trigger a downward breach unless the rssi is 614 // below the entry threshold. There is noise in the measured rssi, and 615 // the kalman-filtered rssi is affected by the trend, so check them both. 616 // TODO(b/74613347) skip this if there are other indications to support the low score 617 int entry = mScoringParams.getEntryRssi(mWifiInfo.getFrequency()); 618 if (mVelocityBasedConnectedScore.getFilteredRssi() >= entry 619 || mWifiInfo.getRssi() >= entry) { 620 // Stay a notch above the transition score to reduce ambiguity. 621 score = transitionScore + 1; 622 } 623 } 624 625 if (mWifiInfo.getScore() >= transitionScore && score < transitionScore) { 626 mLastDownwardBreachTimeMillis = millis; 627 } else if (mWifiInfo.getScore() < transitionScore && score >= transitionScore) { 628 // Staying at below transition score for a certain period of time 629 // to prevent going back to wifi network again in a short time. 630 long elapsedMillis = millis - mLastDownwardBreachTimeMillis; 631 if (elapsedMillis < MIN_TIME_TO_KEEP_BELOW_TRANSITION_SCORE_MILLIS) { 632 score = mWifiInfo.getScore(); 633 } 634 } 635 //sanitize boundaries 636 if (score > maxScore) { 637 score = maxScore; 638 } 639 if (score < 0) { 640 score = 0; 641 } 642 643 if (score < mWifiGlobals.getWifiLowConnectedScoreThresholdToTriggerScanForMbb() 644 && enoughTimePassedSinceLastLowConnectedScoreScan() 645 && mActiveModeWarden.canRequestSecondaryTransientClientModeManager()) { 646 mLastLowScoreScanTimestampMs = mClock.getElapsedSinceBootMillis(); 647 mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); 648 } 649 650 // report score 651 mLegacyIntScore = score; 652 reportNetworkScoreToConnectivityServiceIfNecessary(); 653 updateWifiMetrics(millis, s2); 654 } 655 enoughTimePassedSinceLastLowConnectedScoreScan()656 private boolean enoughTimePassedSinceLastLowConnectedScoreScan() { 657 return mLastLowScoreScanTimestampMs == -1 658 || mClock.getElapsedSinceBootMillis() - mLastLowScoreScanTimestampMs 659 > (mWifiGlobals.getWifiLowConnectedScoreScanPeriodSeconds() * 1000); 660 } 661 getCurrentNetId()662 private int getCurrentNetId() { 663 int netId = 0; 664 if (mNetworkAgent != null) { 665 final Network network = mNetworkAgent.getNetwork(); 666 if (network != null) { 667 netId = network.getNetId(); 668 } 669 } 670 return netId; 671 } 672 673 @Nullable getCurrentNetCapabilities()674 private NetworkCapabilities getCurrentNetCapabilities() { 675 return mNetworkAgent == null ? null : mNetworkAgent.getCurrentNetworkCapabilities(); 676 } 677 getCurrentSessionId()678 private int getCurrentSessionId() { 679 return sessionIdFromNetId(getCurrentNetId()); 680 } 681 682 /** 683 * Encodes a network id into a scoring session id. 684 * 685 * We use a different numeric value for session id and the network id 686 * to make it clear that these are not the same thing. However, for 687 * easier debugging, the network id can be recovered by dropping the 688 * last decimal digit (at least until they get very, very, large). 689 */ sessionIdFromNetId(final int netId)690 public static int sessionIdFromNetId(final int netId) { 691 if (netId <= 0) return INVALID_SESSION_ID; 692 return (int) (((long) netId * 10 + (8 - (netId % 9))) % Integer.MAX_VALUE + 1); 693 } 694 updateWifiMetrics(long now, int s2)695 private void updateWifiMetrics(long now, int s2) { 696 int netId = getCurrentNetId(); 697 698 mAggressiveConnectedScore.updateUsingWifiInfo(mWifiInfo, now); 699 int s1 = ((AggressiveConnectedScore) mAggressiveConnectedScore).generateScore(); 700 logLinkMetrics(now, netId, s1, s2, mLegacyIntScore); 701 702 if (mLegacyIntScore != mWifiInfo.getScore()) { 703 if (mVerboseLoggingEnabled) { 704 Log.d(TAG, "report new wifi score " + mLegacyIntScore); 705 } 706 mWifiInfo.setScore(mLegacyIntScore); 707 } 708 mWifiMetrics.incrementWifiScoreCount(mInterfaceName, mLegacyIntScore); 709 } 710 711 private static final double TIME_CONSTANT_MILLIS = 30.0e+3; 712 private static final long NUD_THROTTLE_MILLIS = 5000; 713 private long mLastKnownNudCheckTimeMillis = 0; 714 private int mLastKnownNudCheckScore = ConnectedScore.WIFI_TRANSITION_SCORE; 715 private int mNudYes = 0; // Counts when we voted for a NUD 716 private int mNudCount = 0; // Counts when we were told a NUD was sent 717 718 /** 719 * Recommends that a layer 3 check be done 720 * 721 * The caller can use this to (help) decide that an IP reachability check 722 * is desirable. The check is not done here; that is the caller's responsibility. 723 * 724 * @return true to indicate that an IP reachability check is recommended 725 */ shouldCheckIpLayer()726 public boolean shouldCheckIpLayer() { 727 // Don't recommend if adaptive connectivity is disabled. 728 if (!mAdaptiveConnectivityEnabledSettingObserver.get() 729 || !mWifiSettingsStore.isWifiScoringEnabled()) { 730 if (mVerboseLoggingEnabled) { 731 Log.d(TAG, "Wifi scoring disabled - Don't check IP layer"); 732 } 733 return false; 734 } 735 long millis = mClock.getWallClockMillis(); 736 long deltaMillis = millis - mLastKnownNudCheckTimeMillis; 737 // Don't ever ask back-to-back - allow at least 5 seconds 738 // for the previous one to finish. 739 if (deltaMillis < NUD_THROTTLE_MILLIS) { 740 return false; 741 } 742 if (SdkLevel.isAtLeastS() && mWifiConnectedNetworkScorerHolder != null) { 743 if (!mWifiConnectedNetworkScorerHolder.getShouldCheckIpLayerOnce()) { 744 return false; 745 } 746 mNudYes++; 747 return true; 748 } 749 int nud = mScoringParams.getNudKnob(); 750 if (nud == 0) { 751 return false; 752 } 753 // nextNudBreach is the bar the score needs to cross before we ask for NUD 754 double nextNudBreach = ConnectedScore.WIFI_TRANSITION_SCORE; 755 if (mWifiConnectedNetworkScorerHolder == null) { 756 // nud is between 1 and 10 at this point 757 double deltaLevel = 11 - nud; 758 // If we were below threshold the last time we checked, then compute a new bar 759 // that starts down from there and decays exponentially back up to the steady-state 760 // bar. If 5 time constants have passed, we are 99% of the way there, so skip the math. 761 if (mLastKnownNudCheckScore < ConnectedScore.WIFI_TRANSITION_SCORE 762 && deltaMillis < 5.0 * TIME_CONSTANT_MILLIS) { 763 double a = Math.exp(-deltaMillis / TIME_CONSTANT_MILLIS); 764 nextNudBreach = 765 a * (mLastKnownNudCheckScore - deltaLevel) + (1.0 - a) * nextNudBreach; 766 } 767 } 768 if (mLegacyIntScore >= nextNudBreach) { 769 return false; 770 } 771 mNudYes++; 772 return true; 773 } 774 775 /** 776 * Should be called when a reachability check has been issued 777 * 778 * When the caller has requested an IP reachability check, calling this will 779 * help to rate-limit requests via shouldCheckIpLayer() 780 */ noteIpCheck()781 public void noteIpCheck() { 782 long millis = mClock.getWallClockMillis(); 783 mLastKnownNudCheckTimeMillis = millis; 784 mLastKnownNudCheckScore = mLegacyIntScore; 785 mNudCount++; 786 // Make sure that only one NUD operation can be triggered. 787 if (mWifiConnectedNetworkScorerHolder != null) { 788 mWifiConnectedNetworkScorerHolder.setShouldCheckIpLayerOnce(false); 789 } 790 } 791 792 /** 793 * Data for dumpsys 794 * 795 * These are stored as csv formatted lines 796 */ 797 private LinkedList<String> mLinkMetricsHistory = new LinkedList<String>(); 798 799 /** 800 * Data logging for dumpsys 801 */ logLinkMetrics(long now, int netId, int s1, int s2, int score)802 private void logLinkMetrics(long now, int netId, int s1, int s2, int score) { 803 if (now < FIRST_REASONABLE_WALL_CLOCK) return; 804 double filteredRssi = -1; 805 double rssiThreshold = -1; 806 if (mWifiConnectedNetworkScorerHolder == null) { 807 filteredRssi = mVelocityBasedConnectedScore.getFilteredRssi(); 808 rssiThreshold = mVelocityBasedConnectedScore.getAdjustedRssiThreshold(); 809 } 810 int freq = mWifiInfo.getFrequency(); 811 int txLinkSpeed = mWifiInfo.getLinkSpeed(); 812 int rxLinkSpeed = mWifiInfo.getRxLinkSpeedMbps(); 813 WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID()); 814 int txThroughputMbps = network.getTxLinkBandwidthKbps() / 1000; 815 int rxThroughputMbps = network.getRxLinkBandwidthKbps() / 1000; 816 double txSuccessRate = mWifiInfo.getSuccessfulTxPacketsPerSecond(); 817 double txRetriesRate = mWifiInfo.getRetriedTxPacketsPerSecond(); 818 double txBadRate = mWifiInfo.getLostTxPacketsPerSecond(); 819 double rxSuccessRate = mWifiInfo.getSuccessfulRxPacketsPerSecond(); 820 long totalBeaconRx = mWifiMetrics.getTotalBeaconRxCount(); 821 String s; 822 try { 823 Calendar c = Calendar.getInstance(); 824 c.setTimeInMillis(now); 825 // Date format: "%tm-%td %tH:%tM:%tS.%tL" 826 String timestamp = new StringBuilder().append(c.get(Calendar.MONTH)).append("-") 827 .append(c.get(Calendar.DAY_OF_MONTH)).append(" ") 828 .append(c.get(Calendar.HOUR_OF_DAY)).append(":") 829 .append(c.get(Calendar.MINUTE)).append(":") 830 .append(c.get(Calendar.SECOND)).append(".") 831 .append(c.get(Calendar.MILLISECOND)).toString(); 832 s = timestamp + "," + mSessionNumber + "," + netId + "," + mWifiInfo.getRssi() 833 + "," + Math.round(filteredRssi * 100) / 100 + "," + rssiThreshold 834 + "," + freq + "," + txLinkSpeed 835 + "," + rxLinkSpeed + "," + txThroughputMbps 836 + "," + rxThroughputMbps + "," + totalBeaconRx 837 + "," + Math.round(txSuccessRate * 100) / 100 838 + "," + Math.round(txRetriesRate * 100) / 100 839 + "," + Math.round(txBadRate * 100) / 100 840 + "," + Math.round(rxSuccessRate * 100) / 100 841 + "," + mNudYes + "," + mNudCount + "," + s1 + "," + s2 + "," + score; 842 } catch (Exception e) { 843 Log.e(TAG, "format problem", e); 844 return; 845 } 846 synchronized (mLinkMetricsHistory) { 847 mLinkMetricsHistory.add(s); 848 while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) { 849 mLinkMetricsHistory.removeFirst(); 850 } 851 } 852 } 853 854 /** 855 * Tag to be used in dumpsys request 856 */ 857 public static final String DUMP_ARG = "WifiScoreReport"; 858 859 /** 860 * Dump logged signal strength and traffic measurements. 861 * @param fd unused 862 * @param pw PrintWriter for writing dump to 863 * @param args unused 864 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)865 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 866 LinkedList<String> history; 867 synchronized (mLinkMetricsHistory) { 868 history = new LinkedList<>(mLinkMetricsHistory); 869 } 870 pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold,freq,txLinkSpeed," 871 + "rxLinkSpeed,txTput,rxTput,bcnCnt,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds," 872 + "s1,s2,score"); 873 for (String line : history) { 874 pw.println(line); 875 } 876 history.clear(); 877 pw.println("externalScorerActive=" + (mWifiConnectedNetworkScorerHolder != null)); 878 pw.println("mShouldReduceNetworkScore=" + mShouldReduceNetworkScore); 879 } 880 881 /** 882 * Set a scorer for Wi-Fi connected network score handling. 883 * @param binder 884 * @param scorer 885 */ setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer)886 public boolean setWifiConnectedNetworkScorer(IBinder binder, 887 IWifiConnectedNetworkScorer scorer) { 888 if (binder == null || scorer == null) return false; 889 // Enforce that only a single scorer can be set successfully. 890 if (mWifiConnectedNetworkScorerHolder != null) { 891 Log.e(TAG, "Failed to set current scorer because one scorer is already set"); 892 return false; 893 } 894 WifiConnectedNetworkScorerHolder scorerHolder = 895 new WifiConnectedNetworkScorerHolder(binder, scorer); 896 if (!scorerHolder.linkScorerToDeath()) { 897 return false; 898 } 899 mWifiConnectedNetworkScorerHolder = scorerHolder; 900 mWifiGlobals.setUsingExternalScorer(true); 901 902 // Register to receive updates from external scorer. 903 mExternalScoreUpdateObserverProxy.registerCallback(mScoreUpdateObserverCallback); 904 905 // Disable AOSP scorer 906 mVelocityBasedConnectedScore = null; 907 mWifiMetrics.setIsExternalWifiScorerOn(true); 908 // If there is already a connection, start a new session 909 final int netId = getCurrentNetId(); 910 if (netId > 0 && !mShouldReduceNetworkScore) { 911 startConnectedNetworkScorer(netId, mIsUserSelected); 912 } 913 return true; 914 } 915 916 /** 917 * Clear an existing scorer for Wi-Fi connected network score handling. 918 */ clearWifiConnectedNetworkScorer()919 public void clearWifiConnectedNetworkScorer() { 920 if (mWifiConnectedNetworkScorerHolder == null) { 921 return; 922 } 923 mWifiConnectedNetworkScorerHolder.reset(); 924 revertToDefaultConnectedScorer(); 925 } 926 927 /** 928 * If this connection is not going to be the default route on the device when cellular is 929 * present, don't send this connection to external scorer for scoring (since scoring only makes 930 * sense if we need to score wifi vs cellular to determine the default network). 931 * 932 * Hence, we ignore local only or restricted wifi connections. 933 * @return true if the connection is local only or restricted, false otherwise. 934 */ isLocalOnlyOrRestrictedConnection()935 private boolean isLocalOnlyOrRestrictedConnection() { 936 final NetworkCapabilities nc = getCurrentNetCapabilities(); 937 if (nc == null) return false; 938 if (SdkLevel.isAtLeastS()) { 939 // restricted connection support only added in S. 940 if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID) 941 || nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) { 942 // restricted connection. 943 Log.v(TAG, "Restricted connection, ignore."); 944 return true; 945 } 946 } 947 if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 948 // local only connection. 949 Log.v(TAG, "Local only connection, ignore."); 950 return true; 951 } 952 return false; 953 } 954 955 /** 956 * Start the registered Wi-Fi connected network scorer. 957 * @param netId identifies the current android.net.Network 958 */ startConnectedNetworkScorer(int netId, boolean isUserSelected)959 public void startConnectedNetworkScorer(int netId, boolean isUserSelected) { 960 mIsUserSelected = isUserSelected; 961 final int sessionId = getCurrentSessionId(); 962 if (mWifiConnectedNetworkScorerHolder == null 963 || netId != getCurrentNetId() 964 || isLocalOnlyOrRestrictedConnection() 965 || sessionId == INVALID_SESSION_ID) { 966 Log.w(TAG, "Cannot start external scoring" 967 + " netId=" + netId 968 + " currentNetId=" + getCurrentNetId() 969 + " currentNetCapabilities=" + getCurrentNetCapabilities() 970 + " sessionId=" + sessionId); 971 return; 972 } 973 mCurrentWifiConfiguration = mWifiConfigManager.getConfiguredNetwork( 974 mWifiInfo.getNetworkId()); 975 mWifiInfo.setScore(isPrimary() ? ConnectedScore.WIFI_MAX_SCORE 976 : ConnectedScore.WIFI_SECONDARY_MAX_SCORE); 977 mWifiConnectedNetworkScorerHolder.startSession(sessionId, mIsUserSelected); 978 mWifiInfoNoReset.setBSSID(mWifiInfo.getBSSID()); 979 mWifiInfoNoReset.setSSID(mWifiInfo.getWifiSsid()); 980 mWifiInfoNoReset.setRssi(mWifiInfo.getRssi()); 981 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 982 mLastScoreBreachHighTimeMillis = INVALID_WALL_CLOCK_MILLIS; 983 } 984 985 /** 986 * Stop the registered Wi-Fi connected network scorer. 987 */ stopConnectedNetworkScorer()988 public void stopConnectedNetworkScorer() { 989 mNetworkAgent = null; 990 if (mWifiConnectedNetworkScorerHolder == null) { 991 return; 992 } 993 mWifiConnectedNetworkScorerHolder.stopSession(); 994 995 long millis = mClock.getWallClockMillis(); 996 // Blocklist the current BSS 997 if ((mLastScoreBreachLowTimeMillis != INVALID_WALL_CLOCK_MILLIS) 998 && ((millis - mLastScoreBreachLowTimeMillis) 999 >= MIN_TIME_TO_WAIT_BEFORE_BLOCKLIST_BSSID_MILLIS)) { 1000 mWifiBlocklistMonitor.handleBssidConnectionFailure(mWifiInfo.getBSSID(), 1001 mCurrentWifiConfiguration, 1002 WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_CONNECTED_SCORE, 1003 mWifiInfo.getRssi()); 1004 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 1005 } 1006 } 1007 1008 /** 1009 * Set NetworkAgent 1010 */ setNetworkAgent(WifiNetworkAgent agent)1011 public void setNetworkAgent(WifiNetworkAgent agent) { 1012 WifiNetworkAgent oldAgent = mNetworkAgent; 1013 mNetworkAgent = agent; 1014 // if mNetworkAgent was null previously, then the score wasn't sent to ConnectivityService. 1015 // Send it now that the NetworkAgent has been set. 1016 if (oldAgent == null && mNetworkAgent != null) { 1017 sendNetworkScore(); 1018 } 1019 } 1020 1021 /** Get cached score */ 1022 @VisibleForTesting 1023 @RequiresApi(Build.VERSION_CODES.S) getScore()1024 public NetworkScore getScore() { 1025 return getScoreBuilder().build(); 1026 } 1027 1028 @RequiresApi(Build.VERSION_CODES.S) getScoreBuilder()1029 private NetworkScore.Builder getScoreBuilder() { 1030 // We should force keep connected for a MBB CMM which is not lingering. 1031 boolean shouldForceKeepConnected = 1032 mCurrentRole == ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT 1033 && !mShouldReduceNetworkScore; 1034 int keepConnectedReason = 1035 shouldForceKeepConnected 1036 ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER 1037 : NetworkScore.KEEP_CONNECTED_NONE; 1038 boolean exiting = SdkLevel.isAtLeastS() && mWifiConnectedNetworkScorerHolder != null 1039 ? !mIsUsable : mLegacyIntScore < ConnectedScore.WIFI_TRANSITION_SCORE; 1040 return new NetworkScore.Builder() 1041 .setLegacyInt(mShouldReduceNetworkScore ? LINGERING_SCORE : mLegacyIntScore) 1042 .setTransportPrimary(mCurrentRole == ActiveModeManager.ROLE_CLIENT_PRIMARY) 1043 .setExiting(exiting) 1044 .setKeepConnectedReason(keepConnectedReason); 1045 } 1046 1047 /** Get legacy int score. */ 1048 @VisibleForTesting 1049 public int getLegacyIntScore() { 1050 // When S Wifi module is run on R: 1051 // - mShouldReduceNetworkScore is useless since MBB doesn't exist on R, so there isn't any 1052 // forced lingering. 1053 // - mIsUsable can't be set as notifyStatusUpdate() for external scorer didn't exist on R 1054 // SDK (assume that only R platform + S Wifi module + R external scorer is possible, 1055 // and R platform + S Wifi module + S external scorer is not possible) 1056 // Thus, it's ok to return the raw int score on R. 1057 return mLegacyIntScore; 1058 } 1059 1060 /** Get counts when we voted for a NUD. */ 1061 @VisibleForTesting 1062 public int getNudYes() { 1063 return mNudYes; 1064 } 1065 1066 private void revertToDefaultConnectedScorer() { 1067 Log.d(TAG, "Using VelocityBasedConnectedScore"); 1068 mVelocityBasedConnectedScore = new VelocityBasedConnectedScore(mScoringParams, mClock); 1069 mWifiConnectedNetworkScorerHolder = null; 1070 mWifiGlobals.setUsingExternalScorer(false); 1071 mExternalScoreUpdateObserverProxy.unregisterCallback(mScoreUpdateObserverCallback); 1072 mWifiMetrics.setIsExternalWifiScorerOn(false); 1073 } 1074 1075 /** 1076 * This is a function of {@link #mCurrentRole} {@link #mShouldReduceNetworkScore}, and 1077 * {@link #mLegacyIntScore}, and should be called when any of them changes. 1078 */ 1079 private void sendNetworkScore() { 1080 if (mNetworkAgent == null) { 1081 return; 1082 } 1083 if (SdkLevel.isAtLeastS()) { 1084 // NetworkScore was introduced in S 1085 mNetworkAgent.sendNetworkScore(getScore()); 1086 } else { 1087 mNetworkAgent.sendNetworkScore(getLegacyIntScore()); 1088 } 1089 } 1090 1091 /** Called when the owner {@link ConcreteClientModeManager}'s role changes. */ 1092 public void onRoleChanged(@Nullable ClientRole role) { 1093 mCurrentRole = role; 1094 if (mAggressiveConnectedScore != null) mAggressiveConnectedScore.onRoleChanged(role); 1095 if (mVelocityBasedConnectedScore != null) mVelocityBasedConnectedScore.onRoleChanged(role); 1096 sendNetworkScore(); 1097 } 1098 } 1099