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 android.net.Network; 20 import android.net.NetworkAgent; 21 import android.net.wifi.IScoreUpdateObserver; 22 import android.net.wifi.IWifiConnectedNetworkScorer; 23 import android.net.wifi.WifiInfo; 24 import android.net.wifi.nl80211.WifiNl80211Manager; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.text.SimpleDateFormat; 32 import java.util.Date; 33 import java.util.LinkedList; 34 import java.util.Locale; 35 36 /** 37 * Class used to calculate scores for connected wifi networks and report it to the associated 38 * network agent. 39 */ 40 public class WifiScoreReport { 41 private static final String TAG = "WifiScoreReport"; 42 43 private static final int DUMPSYS_ENTRY_COUNT_LIMIT = 3600; // 3 hours on 3 second poll 44 45 private boolean mVerboseLoggingEnabled = false; 46 private static final long FIRST_REASONABLE_WALL_CLOCK = 1490000000000L; // mid-December 2016 47 48 private static final long MIN_TIME_TO_KEEP_BELOW_TRANSITION_SCORE_MILLIS = 9000; 49 private long mLastDownwardBreachTimeMillis = 0; 50 51 private static final int WIFI_CONNECTED_NETWORK_SCORER_IDENTIFIER = 0; 52 private static final int INVALID_SESSION_ID = -1; 53 private static final long MIN_TIME_TO_WAIT_BEFORE_BLOCKLIST_BSSID_MILLIS = 29000; 54 private static final long DURATION_TO_BLOCKLIST_BSSID_AFTER_FIRST_EXITING_MILLIS = 30000; 55 private static final long INVALID_WALL_CLOCK_MILLIS = -1; 56 57 // Cache of the last score 58 private int mScore = ConnectedScore.WIFI_MAX_SCORE; 59 60 private final ScoringParams mScoringParams; 61 private final Clock mClock; 62 private int mSessionNumber = 0; // not to be confused with sessionid, this just counts resets 63 private String mInterfaceName; 64 private final BssidBlocklistMonitor mBssidBlocklistMonitor; 65 private long mLastScoreBreachLowTimeMillis = -1; 66 67 ConnectedScore mAggressiveConnectedScore; 68 VelocityBasedConnectedScore mVelocityBasedConnectedScore; 69 70 NetworkAgent mNetworkAgent; 71 WifiMetrics mWifiMetrics; 72 WifiInfo mWifiInfo; 73 WifiNative mWifiNative; 74 WifiThreadRunner mWifiThreadRunner; 75 76 /** 77 * Callback proxy. See {@link android.net.wifi.WifiManager.ScoreUpdateObserver}. 78 */ 79 private class ScoreUpdateObserverProxy extends IScoreUpdateObserver.Stub { 80 @Override notifyScoreUpdate(int sessionId, int score)81 public void notifyScoreUpdate(int sessionId, int score) { 82 mWifiThreadRunner.post(() -> { 83 if (mWifiConnectedNetworkScorerHolder == null 84 || sessionId == INVALID_SESSION_ID 85 || sessionId != getCurrentSessionId()) { 86 Log.w(TAG, "Ignoring stale/invalid external score" 87 + " sessionId=" + sessionId 88 + " currentSessionId=" + getCurrentSessionId() 89 + " score=" + score); 90 return; 91 } 92 if (mNetworkAgent != null) { 93 mNetworkAgent.sendNetworkScore(score); 94 } 95 96 long millis = mClock.getWallClockMillis(); 97 if (score < ConnectedScore.WIFI_TRANSITION_SCORE) { 98 if (mScore >= ConnectedScore.WIFI_TRANSITION_SCORE) { 99 mLastScoreBreachLowTimeMillis = millis; 100 } 101 } else { 102 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 103 } 104 105 mScore = score; 106 updateWifiMetrics(millis, -1, mScore); 107 }); 108 } 109 110 @Override triggerUpdateOfWifiUsabilityStats(int sessionId)111 public void triggerUpdateOfWifiUsabilityStats(int sessionId) { 112 mWifiThreadRunner.post(() -> { 113 if (mWifiConnectedNetworkScorerHolder == null 114 || sessionId == INVALID_SESSION_ID 115 || sessionId != getCurrentSessionId() 116 || mInterfaceName == null) { 117 Log.w(TAG, "Ignoring triggerUpdateOfWifiUsabilityStats" 118 + " sessionId=" + sessionId 119 + " currentSessionId=" + getCurrentSessionId() 120 + " interfaceName=" + mInterfaceName); 121 return; 122 } 123 WifiLinkLayerStats stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName); 124 125 // update mWifiInfo 126 // TODO(b/153075963): Better coordinate this class and ClientModeImpl to remove 127 // redundant codes below and in ClientModeImpl#fetchRssiLinkSpeedAndFrequencyNative. 128 WifiNl80211Manager.SignalPollResult pollResult = 129 mWifiNative.signalPoll(mInterfaceName); 130 if (pollResult != null) { 131 int newRssi = pollResult.currentRssiDbm; 132 int newTxLinkSpeed = pollResult.txBitrateMbps; 133 int newFrequency = pollResult.associationFrequencyMHz; 134 int newRxLinkSpeed = pollResult.rxBitrateMbps; 135 136 if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) { 137 if (newRssi > (WifiInfo.INVALID_RSSI + 256)) { 138 Log.wtf(TAG, "Error! +ve value RSSI: " + newRssi); 139 newRssi -= 256; 140 } 141 mWifiInfo.setRssi(newRssi); 142 } else { 143 mWifiInfo.setRssi(WifiInfo.INVALID_RSSI); 144 } 145 /* 146 * set Tx link speed only if it is valid 147 */ 148 if (newTxLinkSpeed > 0) { 149 mWifiInfo.setLinkSpeed(newTxLinkSpeed); 150 mWifiInfo.setTxLinkSpeedMbps(newTxLinkSpeed); 151 } 152 /* 153 * set Rx link speed only if it is valid 154 */ 155 if (newRxLinkSpeed > 0) { 156 mWifiInfo.setRxLinkSpeedMbps(newRxLinkSpeed); 157 } 158 if (newFrequency > 0) { 159 mWifiInfo.setFrequency(newFrequency); 160 } 161 } 162 163 // TODO(b/153075963): This should not be plumbed through WifiMetrics 164 mWifiMetrics.updateWifiUsabilityStatsEntries(mWifiInfo, stats); 165 }); 166 } 167 } 168 169 /** 170 * Container for storing info about external scorer and tracking its death. 171 */ 172 private final class WifiConnectedNetworkScorerHolder implements IBinder.DeathRecipient { 173 private final IBinder mBinder; 174 private final IWifiConnectedNetworkScorer mScorer; 175 private int mSessionId = INVALID_SESSION_ID; 176 WifiConnectedNetworkScorerHolder(IBinder binder, IWifiConnectedNetworkScorer scorer)177 WifiConnectedNetworkScorerHolder(IBinder binder, IWifiConnectedNetworkScorer scorer) { 178 mBinder = binder; 179 mScorer = scorer; 180 } 181 182 /** 183 * Link WiFi connected scorer to death listener. 184 */ linkScorerToDeath()185 public boolean linkScorerToDeath() { 186 try { 187 mBinder.linkToDeath(this, 0); 188 } catch (RemoteException e) { 189 Log.e(TAG, "Unable to linkToDeath Wifi connected network scorer " + mScorer, e); 190 return false; 191 } 192 return true; 193 } 194 195 /** 196 * App hosting the binder has died. 197 */ 198 @Override binderDied()199 public void binderDied() { 200 mWifiThreadRunner.post(() -> revertToDefaultConnectedScorer()); 201 } 202 203 /** 204 * Unlink this object from binder death. 205 */ reset()206 public void reset() { 207 mBinder.unlinkToDeath(this, 0); 208 } 209 210 /** 211 * Starts a new scoring session. 212 */ startSession(int sessionId)213 public void startSession(int sessionId) { 214 if (sessionId == INVALID_SESSION_ID) { 215 throw new IllegalArgumentException(); 216 } 217 if (mSessionId != INVALID_SESSION_ID) { 218 // This is not expected to happen, log if it does 219 Log.e(TAG, "Stopping session " + mSessionId + " before starting " + sessionId); 220 stopSession(); 221 } 222 // Bail now if the scorer has gone away 223 if (this != mWifiConnectedNetworkScorerHolder) { 224 return; 225 } 226 mSessionId = sessionId; 227 try { 228 mScorer.onStart(sessionId); 229 } catch (RemoteException e) { 230 Log.e(TAG, "Unable to start Wifi connected network scorer " + this, e); 231 revertToDefaultConnectedScorer(); 232 } 233 } stopSession()234 public void stopSession() { 235 final int sessionId = mSessionId; 236 if (sessionId == INVALID_SESSION_ID) return; 237 mSessionId = INVALID_SESSION_ID; 238 try { 239 mScorer.onStop(sessionId); 240 } catch (RemoteException e) { 241 Log.e(TAG, "Unable to stop Wifi connected network scorer " + this, e); 242 revertToDefaultConnectedScorer(); 243 } 244 } 245 } 246 247 private final ScoreUpdateObserverProxy mScoreUpdateObserver = 248 new ScoreUpdateObserverProxy(); 249 250 private WifiConnectedNetworkScorerHolder mWifiConnectedNetworkScorerHolder; 251 WifiScoreReport(ScoringParams scoringParams, Clock clock, WifiMetrics wifiMetrics, WifiInfo wifiInfo, WifiNative wifiNative, BssidBlocklistMonitor bssidBlocklistMonitor, WifiThreadRunner wifiThreadRunner)252 WifiScoreReport(ScoringParams scoringParams, Clock clock, WifiMetrics wifiMetrics, 253 WifiInfo wifiInfo, WifiNative wifiNative, BssidBlocklistMonitor bssidBlocklistMonitor, 254 WifiThreadRunner wifiThreadRunner) { 255 mScoringParams = scoringParams; 256 mClock = clock; 257 mAggressiveConnectedScore = new AggressiveConnectedScore(scoringParams, clock); 258 mVelocityBasedConnectedScore = new VelocityBasedConnectedScore(scoringParams, clock); 259 mWifiMetrics = wifiMetrics; 260 mWifiInfo = wifiInfo; 261 mWifiNative = wifiNative; 262 mBssidBlocklistMonitor = bssidBlocklistMonitor; 263 mWifiThreadRunner = wifiThreadRunner; 264 } 265 266 /** 267 * Reset the last calculated score. 268 */ reset()269 public void reset() { 270 mSessionNumber++; 271 mScore = ConnectedScore.WIFI_MAX_SCORE; 272 mLastKnownNudCheckScore = ConnectedScore.WIFI_TRANSITION_SCORE; 273 mAggressiveConnectedScore.reset(); 274 if (mVelocityBasedConnectedScore != null) { 275 mVelocityBasedConnectedScore.reset(); 276 } 277 mLastDownwardBreachTimeMillis = 0; 278 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 279 if (mVerboseLoggingEnabled) Log.d(TAG, "reset"); 280 } 281 282 /** 283 * Enable/Disable verbose logging in score report generation. 284 */ enableVerboseLogging(boolean enable)285 public void enableVerboseLogging(boolean enable) { 286 mVerboseLoggingEnabled = enable; 287 } 288 289 /** 290 * Calculate wifi network score based on updated link layer stats and send the score to 291 * the WifiNetworkAgent. 292 * 293 * If the score has changed from the previous value, update the WifiNetworkAgent. 294 * 295 * Called periodically (POLL_RSSI_INTERVAL_MSECS) about every 3 seconds. 296 */ calculateAndReportScore()297 public void calculateAndReportScore() { 298 // Bypass AOSP scorer if Wifi connected network scorer is set 299 if (mWifiConnectedNetworkScorerHolder != null) { 300 return; 301 } 302 303 if (mWifiInfo.getRssi() == mWifiInfo.INVALID_RSSI) { 304 Log.d(TAG, "Not reporting score because RSSI is invalid"); 305 return; 306 } 307 int score; 308 309 long millis = mClock.getWallClockMillis(); 310 mVelocityBasedConnectedScore.updateUsingWifiInfo(mWifiInfo, millis); 311 312 int s2 = mVelocityBasedConnectedScore.generateScore(); 313 score = s2; 314 315 if (mWifiInfo.getScore() > ConnectedScore.WIFI_TRANSITION_SCORE 316 && score <= ConnectedScore.WIFI_TRANSITION_SCORE 317 && mWifiInfo.getSuccessfulTxPacketsPerSecond() 318 >= mScoringParams.getYippeeSkippyPacketsPerSecond() 319 && mWifiInfo.getSuccessfulRxPacketsPerSecond() 320 >= mScoringParams.getYippeeSkippyPacketsPerSecond() 321 ) { 322 score = ConnectedScore.WIFI_TRANSITION_SCORE + 1; 323 } 324 325 if (mWifiInfo.getScore() > ConnectedScore.WIFI_TRANSITION_SCORE 326 && score <= ConnectedScore.WIFI_TRANSITION_SCORE) { 327 // We don't want to trigger a downward breach unless the rssi is 328 // below the entry threshold. There is noise in the measured rssi, and 329 // the kalman-filtered rssi is affected by the trend, so check them both. 330 // TODO(b/74613347) skip this if there are other indications to support the low score 331 int entry = mScoringParams.getEntryRssi(mWifiInfo.getFrequency()); 332 if (mVelocityBasedConnectedScore.getFilteredRssi() >= entry 333 || mWifiInfo.getRssi() >= entry) { 334 // Stay a notch above the transition score to reduce ambiguity. 335 score = ConnectedScore.WIFI_TRANSITION_SCORE + 1; 336 } 337 } 338 339 if (mWifiInfo.getScore() >= ConnectedScore.WIFI_TRANSITION_SCORE 340 && score < ConnectedScore.WIFI_TRANSITION_SCORE) { 341 mLastDownwardBreachTimeMillis = millis; 342 } else if (mWifiInfo.getScore() < ConnectedScore.WIFI_TRANSITION_SCORE 343 && score >= ConnectedScore.WIFI_TRANSITION_SCORE) { 344 // Staying at below transition score for a certain period of time 345 // to prevent going back to wifi network again in a short time. 346 long elapsedMillis = millis - mLastDownwardBreachTimeMillis; 347 if (elapsedMillis < MIN_TIME_TO_KEEP_BELOW_TRANSITION_SCORE_MILLIS) { 348 score = mWifiInfo.getScore(); 349 } 350 } 351 //sanitize boundaries 352 if (score > ConnectedScore.WIFI_MAX_SCORE) { 353 score = ConnectedScore.WIFI_MAX_SCORE; 354 } 355 if (score < 0) { 356 score = 0; 357 } 358 359 //report score 360 if (score != mWifiInfo.getScore()) { 361 if (mNetworkAgent != null) { 362 mNetworkAgent.sendNetworkScore(score); 363 } 364 } 365 366 updateWifiMetrics(millis, s2, score); 367 mScore = score; 368 } 369 getCurrentNetId()370 private int getCurrentNetId() { 371 int netId = 0; 372 if (mNetworkAgent != null) { 373 final Network network = mNetworkAgent.getNetwork(); 374 if (network != null) { 375 netId = network.getNetId(); 376 } 377 } 378 return netId; 379 } 380 getCurrentSessionId()381 private int getCurrentSessionId() { 382 return sessionIdFromNetId(getCurrentNetId()); 383 } 384 385 /** 386 * Encodes a network id into a scoring session id. 387 * 388 * We use a different numeric value for session id and the network id 389 * to make it clear that these are not the same thing. However, for 390 * easier debugging, the network id can be recovered by dropping the 391 * last decimal digit (at least until they get very, very, large). 392 */ sessionIdFromNetId(final int netId)393 public static int sessionIdFromNetId(final int netId) { 394 if (netId <= 0) return INVALID_SESSION_ID; 395 return (int) (((long) netId * 10 + (8 - (netId % 9))) % Integer.MAX_VALUE + 1); 396 } 397 updateWifiMetrics(long now, int s2, int score)398 private void updateWifiMetrics(long now, int s2, int score) { 399 int netId = getCurrentNetId(); 400 401 mAggressiveConnectedScore.updateUsingWifiInfo(mWifiInfo, now); 402 int s1 = mAggressiveConnectedScore.generateScore(); 403 logLinkMetrics(now, netId, s1, s2, score); 404 405 if (score != mWifiInfo.getScore()) { 406 if (mVerboseLoggingEnabled) { 407 Log.d(TAG, "report new wifi score " + score); 408 } 409 mWifiInfo.setScore(score); 410 } 411 mWifiMetrics.incrementWifiScoreCount(score); 412 } 413 414 private static final double TIME_CONSTANT_MILLIS = 30.0e+3; 415 private static final long NUD_THROTTLE_MILLIS = 5000; 416 private long mLastKnownNudCheckTimeMillis = 0; 417 private int mLastKnownNudCheckScore = ConnectedScore.WIFI_TRANSITION_SCORE; 418 private int mNudYes = 0; // Counts when we voted for a NUD 419 private int mNudCount = 0; // Counts when we were told a NUD was sent 420 421 /** 422 * Recommends that a layer 3 check be done 423 * 424 * The caller can use this to (help) decide that an IP reachability check 425 * is desirable. The check is not done here; that is the caller's responsibility. 426 * 427 * @return true to indicate that an IP reachability check is recommended 428 */ shouldCheckIpLayer()429 public boolean shouldCheckIpLayer() { 430 int nud = mScoringParams.getNudKnob(); 431 if (nud == 0) { 432 return false; 433 } 434 long millis = mClock.getWallClockMillis(); 435 long deltaMillis = millis - mLastKnownNudCheckTimeMillis; 436 // Don't ever ask back-to-back - allow at least 5 seconds 437 // for the previous one to finish. 438 if (deltaMillis < NUD_THROTTLE_MILLIS) { 439 return false; 440 } 441 // nextNudBreach is the bar the score needs to cross before we ask for NUD 442 double nextNudBreach = ConnectedScore.WIFI_TRANSITION_SCORE; 443 if (mWifiConnectedNetworkScorerHolder == null) { 444 // nud is between 1 and 10 at this point 445 double deltaLevel = 11 - nud; 446 // If we were below threshold the last time we checked, then compute a new bar 447 // that starts down from there and decays exponentially back up to the steady-state 448 // bar. If 5 time constants have passed, we are 99% of the way there, so skip the math. 449 if (mLastKnownNudCheckScore < ConnectedScore.WIFI_TRANSITION_SCORE 450 && deltaMillis < 5.0 * TIME_CONSTANT_MILLIS) { 451 double a = Math.exp(-deltaMillis / TIME_CONSTANT_MILLIS); 452 nextNudBreach = 453 a * (mLastKnownNudCheckScore - deltaLevel) + (1.0 - a) * nextNudBreach; 454 } 455 } 456 if (mScore >= nextNudBreach) { 457 return false; 458 } 459 mNudYes++; 460 return true; 461 } 462 463 /** 464 * Should be called when a reachability check has been issued 465 * 466 * When the caller has requested an IP reachability check, calling this will 467 * help to rate-limit requests via shouldCheckIpLayer() 468 */ noteIpCheck()469 public void noteIpCheck() { 470 long millis = mClock.getWallClockMillis(); 471 mLastKnownNudCheckTimeMillis = millis; 472 mLastKnownNudCheckScore = mScore; 473 mNudCount++; 474 } 475 476 /** 477 * Data for dumpsys 478 * 479 * These are stored as csv formatted lines 480 */ 481 private LinkedList<String> mLinkMetricsHistory = new LinkedList<String>(); 482 483 /** 484 * Data logging for dumpsys 485 */ logLinkMetrics(long now, int netId, int s1, int s2, int score)486 private void logLinkMetrics(long now, int netId, int s1, int s2, int score) { 487 if (now < FIRST_REASONABLE_WALL_CLOCK) return; 488 double rssi = mWifiInfo.getRssi(); 489 double filteredRssi = -1; 490 double rssiThreshold = -1; 491 if (mWifiConnectedNetworkScorerHolder == null) { 492 filteredRssi = mVelocityBasedConnectedScore.getFilteredRssi(); 493 rssiThreshold = mVelocityBasedConnectedScore.getAdjustedRssiThreshold(); 494 } 495 int freq = mWifiInfo.getFrequency(); 496 int txLinkSpeed = mWifiInfo.getLinkSpeed(); 497 int rxLinkSpeed = mWifiInfo.getRxLinkSpeedMbps(); 498 double txSuccessRate = mWifiInfo.getSuccessfulTxPacketsPerSecond(); 499 double txRetriesRate = mWifiInfo.getRetriedTxPacketsPerSecond(); 500 double txBadRate = mWifiInfo.getLostTxPacketsPerSecond(); 501 double rxSuccessRate = mWifiInfo.getSuccessfulRxPacketsPerSecond(); 502 String s; 503 try { 504 String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now)); 505 s = String.format(Locale.US, // Use US to avoid comma/decimal confusion 506 "%s,%d,%d,%.1f,%.1f,%.1f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d,%d", 507 timestamp, mSessionNumber, netId, 508 rssi, filteredRssi, rssiThreshold, freq, txLinkSpeed, rxLinkSpeed, 509 txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate, 510 mNudYes, mNudCount, 511 s1, s2, score); 512 } catch (Exception e) { 513 Log.e(TAG, "format problem", e); 514 return; 515 } 516 synchronized (mLinkMetricsHistory) { 517 mLinkMetricsHistory.add(s); 518 while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) { 519 mLinkMetricsHistory.removeFirst(); 520 } 521 } 522 } 523 524 /** 525 * Tag to be used in dumpsys request 526 */ 527 public static final String DUMP_ARG = "WifiScoreReport"; 528 529 /** 530 * Dump logged signal strength and traffic measurements. 531 * @param fd unused 532 * @param pw PrintWriter for writing dump to 533 * @param args unused 534 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)535 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 536 LinkedList<String> history; 537 synchronized (mLinkMetricsHistory) { 538 history = new LinkedList<>(mLinkMetricsHistory); 539 } 540 pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold,freq,txLinkSpeed," 541 + "rxLinkSpeed,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,s1,s2,score"); 542 for (String line : history) { 543 pw.println(line); 544 } 545 history.clear(); 546 } 547 548 /** 549 * Set a scorer for Wi-Fi connected network score handling. 550 * @param binder 551 * @param scorer 552 */ setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer)553 public boolean setWifiConnectedNetworkScorer(IBinder binder, 554 IWifiConnectedNetworkScorer scorer) { 555 if (binder == null || scorer == null) return false; 556 // Enforce that only a single scorer can be set successfully. 557 if (mWifiConnectedNetworkScorerHolder != null) { 558 Log.e(TAG, "Failed to set current scorer because one scorer is already set"); 559 return false; 560 } 561 WifiConnectedNetworkScorerHolder scorerHolder = 562 new WifiConnectedNetworkScorerHolder(binder, scorer); 563 if (!scorerHolder.linkScorerToDeath()) { 564 return false; 565 } 566 mWifiConnectedNetworkScorerHolder = scorerHolder; 567 568 try { 569 scorer.onSetScoreUpdateObserver(mScoreUpdateObserver); 570 } catch (RemoteException e) { 571 Log.e(TAG, "Unable to set score update observer " + scorer, e); 572 revertToDefaultConnectedScorer(); 573 return false; 574 } 575 // Disable AOSP scorer 576 mVelocityBasedConnectedScore = null; 577 mWifiMetrics.setIsExternalWifiScorerOn(true); 578 // If there is already a connection, start a new session 579 final int netId = getCurrentNetId(); 580 if (netId > 0) { 581 startConnectedNetworkScorer(netId); 582 } 583 return true; 584 } 585 586 /** 587 * Clear an existing scorer for Wi-Fi connected network score handling. 588 */ clearWifiConnectedNetworkScorer()589 public void clearWifiConnectedNetworkScorer() { 590 if (mWifiConnectedNetworkScorerHolder == null) { 591 return; 592 } 593 mWifiConnectedNetworkScorerHolder.reset(); 594 revertToDefaultConnectedScorer(); 595 } 596 597 /** 598 * Start the registered Wi-Fi connected network scorer. 599 * @param netId identifies the current android.net.Network 600 */ startConnectedNetworkScorer(int netId)601 public void startConnectedNetworkScorer(int netId) { 602 final int sessionId = getCurrentSessionId(); 603 if (mWifiConnectedNetworkScorerHolder == null 604 || netId != getCurrentNetId() 605 || sessionId == INVALID_SESSION_ID) { 606 Log.w(TAG, "Cannot start external scoring" 607 + " netId=" + netId 608 + " currentNetId=" + getCurrentNetId() 609 + " sessionId=" + sessionId); 610 return; 611 } 612 mWifiConnectedNetworkScorerHolder.startSession(sessionId); 613 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 614 } 615 616 /** 617 * Stop the registered Wi-Fi connected network scorer. 618 */ stopConnectedNetworkScorer()619 public void stopConnectedNetworkScorer() { 620 mNetworkAgent = null; 621 if (mWifiConnectedNetworkScorerHolder == null) { 622 return; 623 } 624 mWifiConnectedNetworkScorerHolder.stopSession(); 625 626 long millis = mClock.getWallClockMillis(); 627 // Blocklist the current BSS 628 if ((mLastScoreBreachLowTimeMillis != INVALID_WALL_CLOCK_MILLIS) 629 && ((millis - mLastScoreBreachLowTimeMillis) 630 >= MIN_TIME_TO_WAIT_BEFORE_BLOCKLIST_BSSID_MILLIS)) { 631 mBssidBlocklistMonitor.blockBssidForDurationMs(mWifiInfo.getBSSID(), 632 mWifiInfo.getSSID(), 633 DURATION_TO_BLOCKLIST_BSSID_AFTER_FIRST_EXITING_MILLIS); 634 mLastScoreBreachLowTimeMillis = INVALID_WALL_CLOCK_MILLIS; 635 } 636 } 637 638 /** 639 * Set NetworkAgent 640 */ setNetworkAgent(NetworkAgent agent)641 public void setNetworkAgent(NetworkAgent agent) { 642 mNetworkAgent = agent; 643 } 644 645 /** 646 * Get cached score 647 */ getScore()648 public int getScore() { 649 return mScore; 650 } 651 652 /** 653 * Set interface name 654 * @param ifaceName 655 */ setInterfaceName(String ifaceName)656 public void setInterfaceName(String ifaceName) { 657 mInterfaceName = ifaceName; 658 } 659 revertToDefaultConnectedScorer()660 private void revertToDefaultConnectedScorer() { 661 Log.d(TAG, "Using VelocityBasedConnectedScore"); 662 mVelocityBasedConnectedScore = new VelocityBasedConnectedScore(mScoringParams, mClock); 663 mWifiConnectedNetworkScorerHolder = null; 664 mWifiMetrics.setIsExternalWifiScorerOn(false); 665 } 666 } 667