1 /* 2 * Copyright 2019 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.WifiInfo.DEFAULT_MAC_ADDRESS; 20 21 import static com.android.server.wifi.WifiScoreCard.TS_NONE; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.app.AlarmManager; 26 import android.content.Context; 27 import android.content.pm.ModuleInfo; 28 import android.content.pm.PackageManager; 29 import android.net.MacAddress; 30 import android.net.wifi.ScanResult; 31 import android.net.wifi.WifiConfiguration; 32 import android.net.wifi.WifiManager; 33 import android.net.wifi.WifiManager.DeviceMobilityState; 34 import android.net.wifi.WifiScanner; 35 import android.os.Build; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.server.wifi.WifiScoreCard.MemoryStore; 40 import com.android.server.wifi.WifiScoreCard.MemoryStoreAccessBase; 41 import com.android.server.wifi.WifiScoreCard.PerNetwork; 42 import com.android.server.wifi.proto.WifiScoreCardProto.SoftwareBuildInfo; 43 import com.android.server.wifi.proto.WifiScoreCardProto.SystemInfoStats; 44 import com.android.server.wifi.proto.WifiStatsLog; 45 import com.android.server.wifi.proto.nano.WifiMetricsProto.HealthMonitorFailureStats; 46 import com.android.server.wifi.proto.nano.WifiMetricsProto.HealthMonitorMetrics; 47 import com.android.server.wifi.scanner.WifiScannerInternal; 48 49 import com.google.protobuf.InvalidProtocolBufferException; 50 51 import java.io.FileDescriptor; 52 import java.io.PrintWriter; 53 import java.lang.annotation.Retention; 54 import java.lang.annotation.RetentionPolicy; 55 import java.util.ArrayList; 56 import java.util.Calendar; 57 import java.util.List; 58 59 import javax.annotation.concurrent.NotThreadSafe; 60 61 /** 62 * Monitor and detect potential WiFi health issue when RSSI is sufficiently high. 63 * There are two detections, daily detection and post-boot detection. 64 * Post-boot detection is to detect abnormal scan/connection behavior change after device reboot 65 * and/or SW build change. 66 * Daily detection is to detect connection and other behavior changes especially after SW change. 67 */ 68 69 @NotThreadSafe 70 public class WifiHealthMonitor { 71 private static final String TAG = "WifiHealthMonitor"; 72 private boolean mVerboseLoggingEnabled = false; 73 public static final String DAILY_DETECTION_TIMER_TAG = 74 "WifiHealthMonitor Schedule Daily Detection Timer"; 75 public static final String POST_BOOT_DETECTION_TIMER_TAG = 76 "WifiHealthMonitor Schedule Post-Boot Detection Timer"; 77 // Package name of WiFi mainline module found from the following adb command 78 // adb shell pm list packages --apex-only| grep wifi 79 private static final String WIFI_APEX_NAME = "com.android.wifi"; 80 private static final String SYSTEM_INFO_DATA_NAME = "systemInfoData"; 81 // The time that device waits after device boot before triggering post-boot detection. 82 // This needs be long enough so that memory read can complete before post-boot detection. 83 private static final int POST_BOOT_DETECTION_WAIT_TIME_MS = 25_000; 84 // The time interval between two daily detections 85 private static final long DAILY_DETECTION_INTERVAL_MS = 24 * 3600_000; 86 public static final int DAILY_DETECTION_HOUR = 23; 87 private static final int DAILY_DETECTION_MIN = 00; 88 private static final long MIN_WAIT_TIME_BEFORE_FIRST_DETECTION_MS = 100_000; 89 // Max interval between pre-boot scan and post-boot scan to qualify post-boot scan detection 90 private static final long MAX_INTERVAL_BETWEEN_TWO_SCAN_MS = 60_000; 91 // The minimum number of BSSIDs that should be found during a normal scan to trigger detection 92 // of an abnormal scan which happens either before or after the normal scan within a short time. 93 // Minimum number of BSSIDs found at 2G with a normal scan 94 private static final int MIN_NUM_BSSID_SCAN_2G = 2; 95 // Minimum number of BSSIDs found above 2G with a normal scan 96 private static final int MIN_NUM_BSSID_SCAN_ABOVE_2G = 2; 97 // Minimum Tx speed in Mbps for disconnection stats collection 98 static final int HEALTH_MONITOR_COUNT_TX_SPEED_MIN_MBPS = 54; 99 // Minimum Tx packet per seconds for disconnection stats collection 100 static final int HEALTH_MONITOR_MIN_TX_PACKET_PER_SEC = 4; 101 private static final long MAX_TIME_SINCE_LAST_CONNECTION_MS = 48 * 3600_000; 102 103 104 private final Context mContext; 105 private final WifiConfigManager mWifiConfigManager; 106 private final WifiScoreCard mWifiScoreCard; 107 private final Clock mClock; 108 private final AlarmManager mAlarmManager; 109 private final RunnerHandler mHandler; 110 private final WifiNative mWifiNative; 111 private final WifiInjector mWifiInjector; 112 private final DeviceConfigFacade mDeviceConfigFacade; 113 private final ActiveModeWarden mActiveModeWarden; 114 private WifiScannerInternal mScanner; 115 private MemoryStore mMemoryStore; 116 private boolean mWifiEnabled; 117 private WifiSystemInfoStats mWifiSystemInfoStats; 118 private ScanStats mFirstScanStats = new ScanStats(); 119 // Detected significant increase of failure stats between daily data and historical data 120 private FailureStats mFailureStatsIncrease = new FailureStats(); 121 // Detected significant decrease of failure stats between daily data and historical data 122 private FailureStats mFailureStatsDecrease = new FailureStats(); 123 // Detected high failure stats from daily data without historical data 124 private FailureStats mFailureStatsHigh = new FailureStats(); 125 private int mNumNetworkSufficientRecentStatsOnly = 0; 126 private int mNumNetworkSufficientRecentPrevStats = 0; 127 private boolean mHasNewDataForWifiMetrics = false; 128 private int mDeviceMobilityState = WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN; 129 130 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 131 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)132 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 133 update(); 134 } 135 136 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)137 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 138 update(); 139 } 140 141 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)142 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 143 update(); 144 } 145 update()146 private void update() { 147 setWifiEnabled(mActiveModeWarden.getPrimaryClientModeManagerNullable() != null); 148 } 149 } 150 WifiHealthMonitor(Context context, WifiInjector wifiInjector, Clock clock, WifiConfigManager wifiConfigManager, WifiScoreCard wifiScoreCard, RunnerHandler handler, WifiNative wifiNative, String l2KeySeed, DeviceConfigFacade deviceConfigFacade, ActiveModeWarden activeModeWarden)151 WifiHealthMonitor(Context context, WifiInjector wifiInjector, Clock clock, 152 WifiConfigManager wifiConfigManager, WifiScoreCard wifiScoreCard, RunnerHandler handler, 153 WifiNative wifiNative, String l2KeySeed, DeviceConfigFacade deviceConfigFacade, 154 ActiveModeWarden activeModeWarden) { 155 mContext = context; 156 mWifiInjector = wifiInjector; 157 mClock = clock; 158 mWifiConfigManager = wifiConfigManager; 159 mWifiScoreCard = wifiScoreCard; 160 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 161 mHandler = handler; 162 mWifiNative = wifiNative; 163 mDeviceConfigFacade = deviceConfigFacade; 164 mActiveModeWarden = activeModeWarden; 165 mWifiSystemInfoStats = new WifiSystemInfoStats(l2KeySeed); 166 mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 167 mHandler.postToFront(() -> mWifiConfigManager 168 .addOnNetworkUpdateListener(new OnNetworkUpdateListener())); 169 } 170 171 /** 172 * Enable/Disable verbose logging. 173 * 174 * @param verbose true to enable and false to disable. 175 */ enableVerboseLogging(boolean verbose)176 public void enableVerboseLogging(boolean verbose) { 177 mVerboseLoggingEnabled = verbose; 178 } 179 180 private final AlarmManager.OnAlarmListener mDailyDetectionListener = 181 new AlarmManager.OnAlarmListener() { 182 public void onAlarm() { 183 dailyDetectionHandler(); 184 } 185 }; 186 187 private final AlarmManager.OnAlarmListener mPostBootDetectionListener = 188 new AlarmManager.OnAlarmListener() { 189 public void onAlarm() { 190 postBootDetectionHandler(); 191 } 192 }; 193 194 /** 195 * Installs a memory store, request read for post-boot detection and set up detection alarms. 196 */ installMemoryStoreSetUpDetectionAlarm(@onNull MemoryStore memoryStore)197 public void installMemoryStoreSetUpDetectionAlarm(@NonNull MemoryStore memoryStore) { 198 if (mMemoryStore == null) { 199 mMemoryStore = memoryStore; 200 Log.i(TAG, "Installing MemoryStore"); 201 } else { 202 mMemoryStore = memoryStore; 203 Log.e(TAG, "Reinstalling MemoryStore"); 204 } 205 requestReadForPostBootDetection(); 206 setFirstHealthDetectionAlarm(); 207 setPostBootDetectionAlarm(); 208 } 209 210 /** 211 * Set WiFi enable state. 212 * During the off->on transition, retrieve scanner. 213 * During the on->off transition, issue MemoryStore write to save data. 214 */ setWifiEnabled(boolean enable)215 private void setWifiEnabled(boolean enable) { 216 if (mWifiEnabled == enable) return; 217 mWifiEnabled = enable; 218 logd("Set WiFi " + (enable ? "enabled" : "disabled")); 219 if (enable) { 220 retrieveWifiScanner(); 221 } else { 222 doWrites(); 223 } 224 } 225 226 /** 227 * Issue MemoryStore write. This should be called from time to time 228 * to save the state to persistent storage. 229 */ doWrites()230 public void doWrites() { 231 mWifiSystemInfoStats.writeToMemory(); 232 } 233 234 /** 235 * Set device mobility state to assist abnormal scan failure detection 236 */ setDeviceMobilityState(@eviceMobilityState int newState)237 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 238 logd("Device mobility state: " + newState); 239 mDeviceMobilityState = newState; 240 mWifiSystemInfoStats.setMobilityState(newState); 241 } 242 243 /** 244 * Get the maximum scan RSSI valid time for scan RSSI search which is done by finding 245 * the maximum RSSI found among all valid scan detail entries of each network's scanDetailCache 246 * If a scanDetail was older than the returned value, it will not be considered valid. 247 */ getScanRssiValidTimeMs()248 public int getScanRssiValidTimeMs() { 249 return (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_STATIONARY) 250 ? mDeviceConfigFacade.getStationaryScanRssiValidTimeMs() : 251 mDeviceConfigFacade.getNonstationaryScanRssiValidTimeMs(); 252 } 253 254 /** 255 * Issue read request to prepare for post-boot detection. 256 */ requestReadForPostBootDetection()257 private void requestReadForPostBootDetection() { 258 mWifiSystemInfoStats.readFromMemory(); 259 // Potential SW change detection may require to update all networks. 260 // Thus read all networks. 261 requestReadAllNetworks(); 262 } 263 264 /** 265 * Helper method to populate WifiScanner handle. This is done lazily because 266 * WifiScanningService is started after WifiService. 267 */ retrieveWifiScanner()268 private void retrieveWifiScanner() { 269 if (mScanner != null) return; 270 mScanner = WifiLocalServices.getService(WifiScannerInternal.class); 271 if (mScanner == null) return; 272 // Register for all single scan results 273 mScanner.registerScanListener( 274 new WifiScannerInternal.ScanListener(new ScanListener(), mHandler)); 275 } 276 277 /** 278 * Handle scan results when scan results come back from WiFi scanner. 279 */ handleScanResults(List<ScanDetail> scanDetails)280 private void handleScanResults(List<ScanDetail> scanDetails) { 281 ScanStats scanStats = mWifiSystemInfoStats.getCurrScanStats(); 282 scanStats.clear(); 283 scanStats.setLastScanTimeMs(mClock.getWallClockMillis()); 284 for (ScanDetail scanDetail : scanDetails) { 285 ScanResult scanResult = scanDetail.getScanResult(); 286 if (scanResult.is24GHz()) { 287 scanStats.incrementNumBssidLastScan2g(); 288 } else { 289 scanStats.incrementNumBssidLastScanAbove2g(); 290 } 291 } 292 if (mFirstScanStats.getLastScanTimeMs() == TS_NONE) { 293 mFirstScanStats.copy(scanStats); 294 } 295 mWifiSystemInfoStats.setChanged(true); 296 logd(" 2G scanResult count: " + scanStats.getNumBssidLastScan2g() 297 + ", Above2g scanResult count: " + scanStats.getNumBssidLastScanAbove2g()); 298 } 299 setFirstHealthDetectionAlarm()300 private void setFirstHealthDetectionAlarm() { 301 long currTimeMs = mClock.getWallClockMillis(); 302 Calendar calendar = Calendar.getInstance(); 303 calendar.setTimeInMillis(currTimeMs); 304 calendar.set(Calendar.HOUR_OF_DAY, DAILY_DETECTION_HOUR); 305 calendar.set(Calendar.MINUTE, DAILY_DETECTION_MIN); 306 long targetTimeMs = calendar.getTimeInMillis(); 307 long waitTimeMs = targetTimeMs - currTimeMs; 308 if (waitTimeMs < MIN_WAIT_TIME_BEFORE_FIRST_DETECTION_MS) { 309 waitTimeMs += DAILY_DETECTION_INTERVAL_MS; 310 } 311 scheduleDailyDetectionAlarm(waitTimeMs); 312 } 313 scheduleDailyDetectionAlarm(long waitTimeMs)314 private void scheduleDailyDetectionAlarm(long waitTimeMs) { 315 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, 316 mClock.getElapsedSinceBootMillis() + waitTimeMs, 317 DAILY_DETECTION_TIMER_TAG, 318 mDailyDetectionListener, mHandler); 319 } 320 setPostBootDetectionAlarm()321 private void setPostBootDetectionAlarm() { 322 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 323 mClock.getElapsedSinceBootMillis() + POST_BOOT_DETECTION_WAIT_TIME_MS, 324 POST_BOOT_DETECTION_TIMER_TAG, 325 mPostBootDetectionListener, mHandler); 326 } 327 328 /** 329 * Dump the internal state of WifiHealthMonitor. 330 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)331 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 332 pw.println("Dump of WifiHealthMonitor"); 333 pw.println("WifiHealthMonitor - Log Begin ----"); 334 pw.println("System Info Stats"); 335 pw.println(mWifiSystemInfoStats); 336 pw.println("configured network connection stats"); 337 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 338 for (WifiConfiguration network : configuredNetworks) { 339 if (isInvalidConfiguredNetwork(network)) continue; 340 boolean isRecentlyConnected = (mClock.getWallClockMillis() - network.lastConnected) 341 < MAX_TIME_SINCE_LAST_CONNECTION_MS; 342 PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(network.SSID); 343 int cntName = WifiScoreCard.CNT_CONNECTION_ATTEMPT; 344 if (perNetwork.getStatsCurrBuild().getCount(cntName) > 0 345 || perNetwork.getRecentStats().getCount(cntName) > 0 346 || isRecentlyConnected) { 347 pw.println(mWifiScoreCard.lookupNetwork(network.SSID)); 348 } 349 } 350 pw.println("networks with failure increase: "); 351 pw.println(mFailureStatsIncrease); 352 pw.println("networks with failure drop: "); 353 pw.println(mFailureStatsDecrease); 354 pw.println("networks with high failure without previous stats: "); 355 pw.println(mFailureStatsHigh); 356 pw.println("WifiHealthMonitor - Log End ----"); 357 } 358 359 /** 360 * Get current wifi mainline module long version code 361 * @Return a non-zero value if version code is available, 0 otherwise. 362 */ getWifiStackVersion()363 public long getWifiStackVersion() { 364 PackageManager packageManager = mContext.getPackageManager(); 365 long wifiStackVersion = 0; 366 try { 367 ModuleInfo wifiModule = packageManager.getModuleInfo( 368 WIFI_APEX_NAME, PackageManager.MODULE_APEX_NAME); 369 String wifiPackageName = wifiModule.getPackageName(); 370 if (wifiPackageName != null) { 371 wifiStackVersion = packageManager.getPackageInfo( 372 wifiPackageName, PackageManager.MATCH_APEX).getLongVersionCode(); 373 } 374 } catch (PackageManager.NameNotFoundException e) { 375 Log.e(TAG, " Hit PackageManager exception", e); 376 } 377 return wifiStackVersion; 378 } 379 dailyDetectionHandler()380 private synchronized void dailyDetectionHandler() { 381 logd("Run daily detection"); 382 // Clear daily detection result 383 mFailureStatsDecrease.clear(); 384 mFailureStatsIncrease.clear(); 385 mFailureStatsHigh.clear(); 386 mNumNetworkSufficientRecentStatsOnly = 0; 387 mNumNetworkSufficientRecentPrevStats = 0; 388 mHasNewDataForWifiMetrics = true; 389 int connectionDurationSec = 0; 390 // Set the alarm for the next day 391 scheduleDailyDetectionAlarm(DAILY_DETECTION_INTERVAL_MS); 392 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 393 for (WifiConfiguration network : configuredNetworks) { 394 if (isInvalidConfiguredNetwork(network)) { 395 continue; 396 } 397 PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(network.SSID); 398 399 int detectionFlag = perNetwork.dailyDetection(mFailureStatsDecrease, 400 mFailureStatsIncrease, mFailureStatsHigh); 401 if (detectionFlag == WifiScoreCard.SUFFICIENT_RECENT_STATS_ONLY) { 402 mNumNetworkSufficientRecentStatsOnly++; 403 } 404 if (detectionFlag == WifiScoreCard.SUFFICIENT_RECENT_PREV_STATS) { 405 mNumNetworkSufficientRecentPrevStats++; 406 } 407 408 connectionDurationSec += perNetwork.getRecentStats().getCount( 409 WifiScoreCard.CNT_CONNECTION_DURATION_SEC); 410 411 logd("before daily update: " + perNetwork); 412 // Update historical stats with dailyStats and clear dailyStats 413 perNetwork.updateAfterDailyDetection(); 414 logd("after daily update: " + perNetwork); 415 } 416 logd("total connection duration: " + connectionDurationSec); 417 logd("#networks w/ sufficient recent stats: " + mNumNetworkSufficientRecentStatsOnly); 418 logd("#networks w/ sufficient recent and prev stats: " 419 + mNumNetworkSufficientRecentPrevStats); 420 // Write metrics to statsd 421 writeToWifiStatsLog(); 422 doWrites(); 423 mWifiScoreCard.doWrites(); 424 } 425 writeToWifiStatsLog()426 private void writeToWifiStatsLog() { 427 writeToWifiStatsLogPerStats(mFailureStatsIncrease, 428 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__ABNORMALITY_TYPE__SIGNIFICANT_INCREASE); 429 writeToWifiStatsLogPerStats(mFailureStatsDecrease, 430 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__ABNORMALITY_TYPE__SIGNIFICANT_DECREASE); 431 writeToWifiStatsLogPerStats(mFailureStatsHigh, 432 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__ABNORMALITY_TYPE__SIMPLY_HIGH); 433 } 434 writeToWifiStatsLogPerStats(FailureStats failureStats, int abnormalityType)435 private void writeToWifiStatsLogPerStats(FailureStats failureStats, int abnormalityType) { 436 int cntAssocRejection = failureStats.getCount(REASON_ASSOC_REJECTION); 437 if (cntAssocRejection > 0) { 438 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 439 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_ASSOCIATION_REJECTION, 440 cntAssocRejection); 441 } 442 int cntAssocTimeout = failureStats.getCount(REASON_ASSOC_TIMEOUT); 443 if (cntAssocTimeout > 0) { 444 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 445 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_ASSOCIATION_TIMEOUT, 446 cntAssocTimeout); 447 } 448 int cntAuthFailure = failureStats.getCount(REASON_AUTH_FAILURE); 449 if (cntAuthFailure > 0) { 450 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 451 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_AUTHENTICATION, 452 cntAuthFailure); 453 } 454 int cntConnectionFailure = failureStats.getCount(REASON_CONNECTION_FAILURE); 455 if (cntConnectionFailure > 0) { 456 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 457 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_CONNECTION, 458 cntConnectionFailure); 459 } 460 int cntDisconnectionNonlocal = failureStats.getCount(REASON_DISCONNECTION_NONLOCAL); 461 if (cntDisconnectionNonlocal > 0) { 462 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 463 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_NON_LOCAL_DISCONNECTION, 464 cntDisconnectionNonlocal); 465 } 466 int cntShortConnectionNonlocal = failureStats.getCount(REASON_SHORT_CONNECTION_NONLOCAL); 467 if (cntShortConnectionNonlocal > 0) { 468 WifiStatsLog.write(WifiStatsLog.WIFI_FAILURE_STAT_REPORTED, abnormalityType, 469 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_SHORT_CONNECTION_DUE_TO_NON_LOCAL_DISCONNECTION, 470 cntShortConnectionNonlocal); 471 } 472 } 473 474 /** 475 * Build HealthMonitor proto for WifiMetrics 476 * @return counts of networks with significant connection failure stats if there is a new 477 * detection, or a empty proto with default values if there is no new detection 478 */ buildProto()479 public synchronized HealthMonitorMetrics buildProto() { 480 if (!mHasNewDataForWifiMetrics) return null; 481 HealthMonitorMetrics metrics = new HealthMonitorMetrics(); 482 metrics.failureStatsIncrease = failureStatsToProto(mFailureStatsIncrease); 483 metrics.failureStatsDecrease = failureStatsToProto(mFailureStatsDecrease); 484 metrics.failureStatsHigh = failureStatsToProto(mFailureStatsHigh); 485 486 metrics.numNetworkSufficientRecentStatsOnly = mNumNetworkSufficientRecentStatsOnly; 487 metrics.numNetworkSufficientRecentPrevStats = mNumNetworkSufficientRecentPrevStats; 488 mHasNewDataForWifiMetrics = false; 489 return metrics; 490 } 491 failureStatsToProto(FailureStats failureStats)492 private HealthMonitorFailureStats failureStatsToProto(FailureStats failureStats) { 493 HealthMonitorFailureStats stats = new HealthMonitorFailureStats(); 494 stats.cntAssocRejection = failureStats.getCount(REASON_ASSOC_REJECTION); 495 stats.cntAssocTimeout = failureStats.getCount(REASON_ASSOC_TIMEOUT); 496 stats.cntAuthFailure = failureStats.getCount(REASON_AUTH_FAILURE); 497 stats.cntConnectionFailure = failureStats.getCount(REASON_CONNECTION_FAILURE); 498 stats.cntDisconnectionNonlocalConnecting = 499 failureStats.getCount(REASON_CONNECTION_FAILURE_DISCONNECTION); 500 stats.cntDisconnectionNonlocal = 501 failureStats.getCount(REASON_DISCONNECTION_NONLOCAL); 502 stats.cntShortConnectionNonlocal = 503 failureStats.getCount(REASON_SHORT_CONNECTION_NONLOCAL); 504 return stats; 505 } 506 isInvalidConfiguredNetwork(WifiConfiguration config)507 private boolean isInvalidConfiguredNetwork(WifiConfiguration config) { 508 return (config == null || WifiManager.UNKNOWN_SSID.equals(config.SSID) 509 || config.SSID == null); 510 } 511 postBootDetectionHandler()512 private void postBootDetectionHandler() { 513 logd("Run post-boot detection"); 514 postBootSwBuildCheck(); 515 mWifiSystemInfoStats.postBootAbnormalScanDetection(mFirstScanStats); 516 logd(" postBootAbnormalScanDetection: " + mWifiSystemInfoStats.getScanFailure()); 517 // TODO: Check if scan is not empty but all high RSSI connection attempts failed 518 // while connection attempt with the same network succeeded before boot. 519 doWrites(); 520 } 521 postBootSwBuildCheck()522 private void postBootSwBuildCheck() { 523 WifiSoftwareBuildInfo currSoftwareBuildInfo = extractCurrentSoftwareBuildInfo(); 524 if (currSoftwareBuildInfo == null) return; 525 logd(currSoftwareBuildInfo.toString()); 526 527 mWifiSystemInfoStats.finishPendingRead(); 528 if (mWifiSystemInfoStats.getCurrSoftwareBuildInfo() == null) { 529 logd("Miss current software build info from memory"); 530 mWifiSystemInfoStats.setCurrSoftwareBuildInfo(currSoftwareBuildInfo); 531 return; 532 } 533 if (mWifiSystemInfoStats.detectSwBuildChange(currSoftwareBuildInfo)) { 534 logd("Detect SW build change"); 535 updateAllNetworkAfterSwBuildChange(); 536 mWifiSystemInfoStats.updateBuildInfoAfterSwBuildChange(currSoftwareBuildInfo); 537 } else { 538 logd("Detect no SW build change"); 539 } 540 } 541 542 /** 543 * Issue NetworkStats read request for all configured networks. 544 */ requestReadAllNetworks()545 private void requestReadAllNetworks() { 546 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 547 for (WifiConfiguration network : configuredNetworks) { 548 if (isInvalidConfiguredNetwork(network)) { 549 continue; 550 } 551 logd(network.SSID); 552 WifiScoreCard.PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(network.SSID); 553 if (perNetwork == null) { 554 // This network is not in cache. Move it to cache and read it out from MemoryStore. 555 mWifiScoreCard.lookupNetwork(network.SSID); 556 } else { 557 // This network is already in cache before memoryStore is stalled. 558 mWifiScoreCard.requestReadNetwork(perNetwork); 559 } 560 } 561 } 562 563 /** 564 * Update NetworkStats of all configured networks after a SW build change is detected 565 */ updateAllNetworkAfterSwBuildChange()566 private void updateAllNetworkAfterSwBuildChange() { 567 List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks(); 568 for (WifiConfiguration network : configuredNetworks) { 569 if (isInvalidConfiguredNetwork(network)) { 570 continue; 571 } 572 logd(network.SSID); 573 WifiScoreCard.PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(network.SSID); 574 575 logd("before SW build update: " + perNetwork); 576 perNetwork.updateAfterSwBuildChange(); 577 logd("after SW build update: " + perNetwork); 578 } 579 } 580 581 /** 582 * Extract current software build information from the running software. 583 */ extractCurrentSoftwareBuildInfo()584 private WifiSoftwareBuildInfo extractCurrentSoftwareBuildInfo() { 585 if (!mWifiEnabled) { 586 return null; 587 } 588 long wifiStackVersion = getWifiStackVersion(); 589 String osBuildVersion = replaceNullByEmptyString(Build.DISPLAY); 590 if (mWifiNative == null) { 591 return null; 592 } 593 String driverVersion = replaceNullByEmptyString(mWifiNative.getDriverVersion()); 594 String firmwareVersion = replaceNullByEmptyString(mWifiNative.getFirmwareVersion()); 595 return (new WifiSoftwareBuildInfo(osBuildVersion, 596 wifiStackVersion, driverVersion, firmwareVersion)); 597 } 598 replaceNullByEmptyString(String str)599 private String replaceNullByEmptyString(String str) { 600 return str == null ? "" : str; 601 } 602 603 /** 604 * Clears the internal state. 605 * This is called in response to a factoryReset call from Settings. 606 */ clear()607 public void clear() { 608 mWifiSystemInfoStats.clearAll(); 609 } 610 611 public static final int REASON_NO_FAILURE = -1; 612 public static final int REASON_ASSOC_REJECTION = 0; 613 public static final int REASON_ASSOC_TIMEOUT = 1; 614 public static final int REASON_AUTH_FAILURE = 2; 615 public static final int REASON_CONNECTION_FAILURE = 3; 616 public static final int REASON_DISCONNECTION_NONLOCAL = 4; 617 public static final int REASON_SHORT_CONNECTION_NONLOCAL = 5; 618 public static final int REASON_CONNECTION_FAILURE_DISCONNECTION = 6; 619 public static final int NUMBER_FAILURE_REASON_CODE = 7; 620 public static final String[] FAILURE_REASON_NAME = { 621 "association rejection failure", 622 "association timeout failure", 623 "authentication failure", 624 "connection failure", 625 "disconnection", 626 "short connection", 627 "connection failure disconnection", 628 }; 629 @IntDef(prefix = { "REASON_" }, value = { 630 REASON_NO_FAILURE, 631 REASON_ASSOC_REJECTION, 632 REASON_ASSOC_TIMEOUT, 633 REASON_AUTH_FAILURE, 634 REASON_CONNECTION_FAILURE, 635 REASON_DISCONNECTION_NONLOCAL, 636 REASON_SHORT_CONNECTION_NONLOCAL, 637 REASON_CONNECTION_FAILURE_DISCONNECTION 638 }) 639 @Retention(RetentionPolicy.SOURCE) 640 public @interface FailureReasonCode {} 641 642 /** 643 * A class maintaining the number of networks with high failure rate or 644 * with a significant change of failure rate 645 */ 646 public static class FailureStats { 647 private final int[] mCount = new int[NUMBER_FAILURE_REASON_CODE]; clear()648 void clear() { 649 for (int i = 0; i < NUMBER_FAILURE_REASON_CODE; i++) { 650 mCount[i] = 0; 651 } 652 } 653 getCount(@ailureReasonCode int reasonCode)654 int getCount(@FailureReasonCode int reasonCode) { 655 return mCount[reasonCode]; 656 } 657 setCount(@ailureReasonCode int reasonCode, int cnt)658 void setCount(@FailureReasonCode int reasonCode, int cnt) { 659 mCount[reasonCode] = cnt; 660 } 661 incrementCount(@ailureReasonCode int reasonCode)662 void incrementCount(@FailureReasonCode int reasonCode) { 663 mCount[reasonCode]++; 664 } 665 666 @Override toString()667 public String toString() { 668 StringBuilder sb = new StringBuilder(); 669 for (int i = 0; i < NUMBER_FAILURE_REASON_CODE; i++) { 670 if (mCount[i] == 0) continue; 671 sb.append(FAILURE_REASON_NAME[i]).append(": ").append(mCount[i]).append(" "); 672 } 673 return sb.toString(); 674 } 675 } 676 /** 677 * A class maintaining current OS, Wifi APK, Wifi driver and firmware build version information. 678 */ 679 final class WifiSoftwareBuildInfo { 680 private String mOsBuildVersion; 681 private long mWifiStackVersion; 682 private String mWifiDriverVersion; 683 private String mWifiFirmwareVersion; WifiSoftwareBuildInfo(@onNull String osBuildVersion, long wifiStackVersion, @NonNull String wifiDriverVersion, @NonNull String wifiFirmwareVersion)684 WifiSoftwareBuildInfo(@NonNull String osBuildVersion, long wifiStackVersion, 685 @NonNull String wifiDriverVersion, @NonNull String wifiFirmwareVersion) { 686 mOsBuildVersion = osBuildVersion; 687 mWifiStackVersion = wifiStackVersion; 688 mWifiDriverVersion = wifiDriverVersion; 689 mWifiFirmwareVersion = wifiFirmwareVersion; 690 } WifiSoftwareBuildInfo(@onNull WifiSoftwareBuildInfo wifiSoftwareBuildInfo)691 WifiSoftwareBuildInfo(@NonNull WifiSoftwareBuildInfo wifiSoftwareBuildInfo) { 692 mOsBuildVersion = wifiSoftwareBuildInfo.getOsBuildVersion(); 693 mWifiStackVersion = wifiSoftwareBuildInfo.getWifiStackVersion(); 694 mWifiDriverVersion = wifiSoftwareBuildInfo.getWifiDriverVersion(); 695 mWifiFirmwareVersion = wifiSoftwareBuildInfo.getWifiFirmwareVersion(); 696 } getOsBuildVersion()697 String getOsBuildVersion() { 698 return mOsBuildVersion; 699 } getWifiStackVersion()700 long getWifiStackVersion() { 701 return mWifiStackVersion; 702 } getWifiDriverVersion()703 String getWifiDriverVersion() { 704 return mWifiDriverVersion; 705 } getWifiFirmwareVersion()706 String getWifiFirmwareVersion() { 707 return mWifiFirmwareVersion; 708 } 709 @Override equals(Object otherObj)710 public boolean equals(Object otherObj) { 711 if (this == otherObj) { 712 return true; 713 } 714 if (!(otherObj instanceof WifiSoftwareBuildInfo)) { 715 return false; 716 } 717 if (otherObj == null) { 718 return false; 719 } 720 WifiSoftwareBuildInfo other = (WifiSoftwareBuildInfo) otherObj; 721 return mOsBuildVersion.equals(other.getOsBuildVersion()) 722 && mWifiStackVersion == other.getWifiStackVersion() 723 && mWifiDriverVersion.equals(other.getWifiDriverVersion()) 724 && mWifiFirmwareVersion.equals(other.getWifiFirmwareVersion()); 725 } 726 @Override toString()727 public String toString() { 728 StringBuilder sb = new StringBuilder(); 729 sb.append("OS build version: "); 730 sb.append(mOsBuildVersion); 731 sb.append(" Wifi stack version: "); 732 sb.append(mWifiStackVersion); 733 sb.append(" Wifi driver version: "); 734 sb.append(mWifiDriverVersion); 735 sb.append(" Wifi firmware version: "); 736 sb.append(mWifiFirmwareVersion); 737 return sb.toString(); 738 } 739 } 740 741 /** 742 * A class maintaining various WiFi system information and statistics. 743 */ 744 final class WifiSystemInfoStats extends MemoryStoreAccessBase { 745 private WifiSoftwareBuildInfo mCurrSoftwareBuildInfo; 746 private WifiSoftwareBuildInfo mPrevSoftwareBuildInfo; 747 private ScanStats mCurrScanStats = new ScanStats(); 748 private ScanStats mPrevScanStats = new ScanStats(); 749 private int mScanFailure; 750 private @DeviceMobilityState int mMobilityState; 751 private boolean mChanged = false; WifiSystemInfoStats(String l2KeySeed)752 WifiSystemInfoStats(String l2KeySeed) { 753 super(WifiScoreCard.computeHashLong( 754 "", MacAddress.fromString(DEFAULT_MAC_ADDRESS), l2KeySeed)); 755 } 756 getCurrScanStats()757 ScanStats getCurrScanStats() { 758 return mCurrScanStats; 759 } 760 setChanged(boolean changed)761 void setChanged(boolean changed) { 762 mChanged = changed; 763 } 764 setCurrSoftwareBuildInfo(WifiSoftwareBuildInfo currSoftwareBuildInfo)765 void setCurrSoftwareBuildInfo(WifiSoftwareBuildInfo currSoftwareBuildInfo) { 766 mCurrSoftwareBuildInfo = currSoftwareBuildInfo; 767 mChanged = true; 768 } 769 setMobilityState(@eviceMobilityState int mobilityState)770 void setMobilityState(@DeviceMobilityState int mobilityState) { 771 mMobilityState = mobilityState; 772 } 773 getCurrSoftwareBuildInfo()774 WifiSoftwareBuildInfo getCurrSoftwareBuildInfo() { 775 return mCurrSoftwareBuildInfo; 776 } 777 getPrevSoftwareBuildInfo()778 WifiSoftwareBuildInfo getPrevSoftwareBuildInfo() { 779 return mPrevSoftwareBuildInfo; 780 } 781 clearAll()782 void clearAll() { 783 mCurrSoftwareBuildInfo = null; 784 mPrevSoftwareBuildInfo = null; 785 mCurrScanStats.clear(); 786 mPrevScanStats.clear(); 787 mChanged = true; 788 } 789 790 /** 791 * Detect if there is a SW build change by comparing current SW build version vs. SW build 792 * version previously saved in MemoryStore. 793 * @param currSoftwareBuildInfo is current SW build info derived from running SW 794 * @return true if a SW build change is detected, false if no change is detected. 795 */ detectSwBuildChange(@onNull WifiSoftwareBuildInfo currSoftwareBuildInfo)796 boolean detectSwBuildChange(@NonNull WifiSoftwareBuildInfo currSoftwareBuildInfo) { 797 if (mCurrSoftwareBuildInfo == null) { 798 return false; 799 } 800 801 logd(" from Memory: " + mCurrSoftwareBuildInfo); 802 logd(" from SW: " + currSoftwareBuildInfo); 803 return (!mCurrSoftwareBuildInfo.equals(currSoftwareBuildInfo)); 804 } 805 updateBuildInfoAfterSwBuildChange(@onNull WifiSoftwareBuildInfo currBuildInfo)806 void updateBuildInfoAfterSwBuildChange(@NonNull WifiSoftwareBuildInfo currBuildInfo) { 807 mPrevSoftwareBuildInfo = new WifiSoftwareBuildInfo(mCurrSoftwareBuildInfo); 808 mCurrSoftwareBuildInfo = new WifiSoftwareBuildInfo(currBuildInfo); 809 mChanged = true; 810 } 811 readFromMemory()812 void readFromMemory() { 813 if (mMemoryStore != null) { 814 mMemoryStore.read(getL2Key(), SYSTEM_INFO_DATA_NAME, 815 (value) -> readBackListener(value)); 816 } 817 } 818 819 // Read may not be completed in theory when finishPendingRead() is called. 820 // Currently it relies on the fact that memory read is issued right after boot complete 821 // while finishPendingRead() is called only POST_BOOT_DETECTION_WAIT_TIME_MS after that. finishPendingRead()822 private void finishPendingRead() { 823 final byte[] serialized = finishPendingReadBytes(); 824 if (serialized == null) { 825 logd("Fail to read systemInfoStats from memory"); 826 return; 827 } 828 SystemInfoStats systemInfoStats; 829 try { 830 systemInfoStats = SystemInfoStats.parseFrom(serialized); 831 } catch (InvalidProtocolBufferException e) { 832 Log.e(TAG, "Failed to deserialize", e); 833 return; 834 } 835 readFromMemory(systemInfoStats); 836 } 837 readFromMemory(@onNull SystemInfoStats systemInfoStats)838 private void readFromMemory(@NonNull SystemInfoStats systemInfoStats) { 839 if (systemInfoStats.hasCurrSoftwareBuildInfo()) { 840 mCurrSoftwareBuildInfo = fromSoftwareBuildInfo( 841 systemInfoStats.getCurrSoftwareBuildInfo()); 842 } 843 if (systemInfoStats.hasPrevSoftwareBuildInfo()) { 844 mPrevSoftwareBuildInfo = fromSoftwareBuildInfo( 845 systemInfoStats.getPrevSoftwareBuildInfo()); 846 } 847 if (systemInfoStats.hasNumBssidLastScan2G()) { 848 mPrevScanStats.setNumBssidLastScan2g(systemInfoStats.getNumBssidLastScan2G()); 849 } 850 if (systemInfoStats.hasNumBssidLastScanAbove2G()) { 851 mPrevScanStats.setNumBssidLastScanAbove2g(systemInfoStats 852 .getNumBssidLastScanAbove2G()); 853 } 854 if (systemInfoStats.hasLastScanTimeMs()) { 855 mPrevScanStats.setLastScanTimeMs(systemInfoStats.getLastScanTimeMs()); 856 } 857 } 858 writeToMemory()859 void writeToMemory() { 860 if (mMemoryStore == null || !mChanged) return; 861 byte[] serialized = toSystemInfoStats().toByteArray(); 862 mMemoryStore.write(getL2Key(), SYSTEM_INFO_DATA_NAME, serialized); 863 mChanged = false; 864 } 865 toSystemInfoStats()866 SystemInfoStats toSystemInfoStats() { 867 SystemInfoStats.Builder builder = SystemInfoStats.newBuilder(); 868 if (mCurrSoftwareBuildInfo != null) { 869 builder.setCurrSoftwareBuildInfo(toSoftwareBuildInfo(mCurrSoftwareBuildInfo)); 870 } 871 if (mPrevSoftwareBuildInfo != null) { 872 builder.setPrevSoftwareBuildInfo(toSoftwareBuildInfo(mPrevSoftwareBuildInfo)); 873 } 874 builder.setLastScanTimeMs(mCurrScanStats.getLastScanTimeMs()); 875 builder.setNumBssidLastScan2G(mCurrScanStats.getNumBssidLastScan2g()); 876 builder.setNumBssidLastScanAbove2G(mCurrScanStats.getNumBssidLastScanAbove2g()); 877 return builder.build(); 878 } 879 toSoftwareBuildInfo( @onNull WifiSoftwareBuildInfo softwareBuildInfo)880 private SoftwareBuildInfo toSoftwareBuildInfo( 881 @NonNull WifiSoftwareBuildInfo softwareBuildInfo) { 882 SoftwareBuildInfo.Builder builder = SoftwareBuildInfo.newBuilder(); 883 builder.setOsBuildVersion(softwareBuildInfo.getOsBuildVersion()); 884 builder.setWifiStackVersion(softwareBuildInfo.getWifiStackVersion()); 885 builder.setWifiDriverVersion(softwareBuildInfo.getWifiDriverVersion()); 886 builder.setWifiFirmwareVersion(softwareBuildInfo.getWifiFirmwareVersion()); 887 return builder.build(); 888 } 889 fromSoftwareBuildInfo( @onNull SoftwareBuildInfo softwareBuildInfo)890 WifiSoftwareBuildInfo fromSoftwareBuildInfo( 891 @NonNull SoftwareBuildInfo softwareBuildInfo) { 892 String osBuildVersion = softwareBuildInfo.hasOsBuildVersion() 893 ? softwareBuildInfo.getOsBuildVersion() : "NA"; 894 long stackVersion = softwareBuildInfo.hasWifiStackVersion() 895 ? softwareBuildInfo.getWifiStackVersion() : 0; 896 String driverVersion = softwareBuildInfo.hasWifiDriverVersion() 897 ? softwareBuildInfo.getWifiDriverVersion() : "NA"; 898 String firmwareVersion = softwareBuildInfo.hasWifiFirmwareVersion() 899 ? softwareBuildInfo.getWifiFirmwareVersion() : "NA"; 900 return new WifiSoftwareBuildInfo(osBuildVersion, stackVersion, 901 driverVersion, firmwareVersion); 902 } 903 /** 904 * Detect pre-boot or post-boot detection failure. 905 * @return 0 if no failure is found or a positive integer if failure is found where 906 * b0 for pre-boot 2G scan failure 907 * b1 for pre-boot Above2g scan failure 908 * b2 for post-boot 2G scan failure 909 * b3 for post-boot Above2g scan failure 910 */ postBootAbnormalScanDetection(ScanStats firstScanStats)911 void postBootAbnormalScanDetection(ScanStats firstScanStats) { 912 long preBootScanTimeMs = mPrevScanStats.getLastScanTimeMs(); 913 long postBootScanTimeMs = firstScanStats.getLastScanTimeMs(); 914 logd(" preBootScanTimeMs: " + preBootScanTimeMs); 915 logd(" postBootScanTimeMs: " + postBootScanTimeMs); 916 int preBootNumBssid2g = mPrevScanStats.getNumBssidLastScan2g(); 917 int preBootNumBssidAbove2g = mPrevScanStats.getNumBssidLastScanAbove2g(); 918 int postBootNumBssid2g = firstScanStats.getNumBssidLastScan2g(); 919 int postBootNumBssidAbove2g = firstScanStats.getNumBssidLastScanAbove2g(); 920 logd(" preBootScan 2G count: " + preBootNumBssid2g 921 + ", Above2G count: " + preBootNumBssidAbove2g); 922 logd(" postBootScan 2G count: " + postBootNumBssid2g 923 + ", Above2G count: " + postBootNumBssidAbove2g); 924 mScanFailure = 0; 925 if (postBootScanTimeMs == TS_NONE || preBootScanTimeMs == TS_NONE) return; 926 if ((postBootScanTimeMs - preBootScanTimeMs) > MAX_INTERVAL_BETWEEN_TWO_SCAN_MS) { 927 return; 928 } 929 if (preBootNumBssid2g == 0 && postBootNumBssid2g >= MIN_NUM_BSSID_SCAN_2G) { 930 mScanFailure += 1; 931 } 932 if (preBootNumBssidAbove2g == 0 && postBootNumBssidAbove2g 933 >= MIN_NUM_BSSID_SCAN_ABOVE_2G) { 934 mScanFailure += 2; 935 } 936 if (postBootNumBssid2g == 0 && preBootNumBssid2g >= MIN_NUM_BSSID_SCAN_2G) { 937 mScanFailure += 4; 938 } 939 if (postBootNumBssidAbove2g == 0 && preBootNumBssidAbove2g 940 >= MIN_NUM_BSSID_SCAN_ABOVE_2G) { 941 mScanFailure += 8; 942 } 943 } 944 getScanFailure()945 int getScanFailure() { 946 return mScanFailure; 947 } 948 949 @Override toString()950 public String toString() { 951 StringBuilder sb = new StringBuilder(); 952 if (mCurrSoftwareBuildInfo != null) { 953 sb.append("current SW build: "); 954 sb.append(mCurrSoftwareBuildInfo); 955 } 956 if (mPrevSoftwareBuildInfo != null) { 957 sb.append("\n"); 958 sb.append("previous SW build: "); 959 sb.append(mPrevSoftwareBuildInfo); 960 } 961 sb.append("\n"); 962 sb.append("currScanStats: "); 963 sb.append(mCurrScanStats); 964 sb.append("\n"); 965 sb.append("prevScanStats: "); 966 sb.append(mPrevScanStats); 967 return sb.toString(); 968 } 969 } 970 971 final class ScanStats { 972 private long mLastScanTimeMs = TS_NONE; 973 private int mNumBssidLastScan2g; 974 private int mNumBssidLastScanAbove2g; copy(ScanStats source)975 void copy(ScanStats source) { 976 mLastScanTimeMs = source.mLastScanTimeMs; 977 mNumBssidLastScan2g = source.mNumBssidLastScan2g; 978 mNumBssidLastScanAbove2g = source.mNumBssidLastScanAbove2g; 979 } setLastScanTimeMs(long lastScanTimeMs)980 void setLastScanTimeMs(long lastScanTimeMs) { 981 mLastScanTimeMs = lastScanTimeMs; 982 } setNumBssidLastScan2g(int numBssidLastScan2g)983 void setNumBssidLastScan2g(int numBssidLastScan2g) { 984 mNumBssidLastScan2g = numBssidLastScan2g; 985 } setNumBssidLastScanAbove2g(int numBssidLastScanAbove2g)986 void setNumBssidLastScanAbove2g(int numBssidLastScanAbove2g) { 987 mNumBssidLastScanAbove2g = numBssidLastScanAbove2g; 988 } getLastScanTimeMs()989 long getLastScanTimeMs() { 990 return mLastScanTimeMs; 991 } getNumBssidLastScan2g()992 int getNumBssidLastScan2g() { 993 return mNumBssidLastScan2g; 994 } getNumBssidLastScanAbove2g()995 int getNumBssidLastScanAbove2g() { 996 return mNumBssidLastScanAbove2g; 997 } incrementNumBssidLastScan2g()998 void incrementNumBssidLastScan2g() { 999 mNumBssidLastScan2g++; 1000 } incrementNumBssidLastScanAbove2g()1001 void incrementNumBssidLastScanAbove2g() { 1002 mNumBssidLastScanAbove2g++; 1003 } clear()1004 void clear() { 1005 mLastScanTimeMs = TS_NONE; 1006 mNumBssidLastScan2g = 0; 1007 mNumBssidLastScanAbove2g = 0; 1008 } 1009 @Override toString()1010 public String toString() { 1011 StringBuilder sb = new StringBuilder(); 1012 sb.append("last scan time: "); 1013 sb.append(mLastScanTimeMs); 1014 sb.append(" APs found at 2G: "); 1015 sb.append(mNumBssidLastScan2g); 1016 sb.append(" APs found above 2g: "); 1017 sb.append(mNumBssidLastScanAbove2g); 1018 return sb.toString(); 1019 } 1020 } 1021 1022 @VisibleForTesting getWifiSystemInfoStats()1023 WifiSystemInfoStats getWifiSystemInfoStats() { 1024 return mWifiSystemInfoStats; 1025 } 1026 logd(String string)1027 private void logd(String string) { 1028 if (mVerboseLoggingEnabled) { 1029 Log.d(TAG, string); 1030 } 1031 } 1032 1033 /** 1034 * Listener for config manager network config related events. 1035 */ 1036 private class OnNetworkUpdateListener implements 1037 WifiConfigManager.OnNetworkUpdateListener { 1038 1039 @Override onNetworkAdded(WifiConfiguration config)1040 public void onNetworkAdded(WifiConfiguration config) { 1041 if (config == null) return; 1042 mWifiScoreCard.lookupNetwork(config.SSID); 1043 } 1044 1045 @Override onNetworkRemoved(WifiConfiguration config)1046 public void onNetworkRemoved(WifiConfiguration config) { 1047 if (config == null || (config.fromWifiNetworkSuggestion && !config.isPasspoint())) { 1048 // If a suggestion non-passpoint network is removed from wifiConfigManager do not 1049 // remove the ScoreCard. That will be removed when suggestion is removed. 1050 return; 1051 } 1052 mWifiScoreCard.removeNetwork(config.SSID); 1053 } 1054 } 1055 1056 /** 1057 * Scan listener for any full band scan. 1058 */ 1059 private class ScanListener implements WifiScanner.ScanListener { 1060 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 1061 clearScanDetails()1062 public void clearScanDetails() { 1063 mScanDetails.clear(); 1064 } 1065 1066 @Override onSuccess()1067 public void onSuccess() { 1068 } 1069 1070 @Override onFailure(int reason, String description)1071 public void onFailure(int reason, String description) { 1072 logd("registerScanListener onFailure:" 1073 + " reason: " + reason + " description: " + description); 1074 } 1075 1076 @Override onPeriodChanged(int periodInMs)1077 public void onPeriodChanged(int periodInMs) { 1078 } 1079 1080 @Override onResults(WifiScanner.ScanData[] results)1081 public void onResults(WifiScanner.ScanData[] results) { 1082 if (!mWifiEnabled || results == null || results.length == 0) { 1083 clearScanDetails(); 1084 return; 1085 } 1086 1087 if (WifiScanner.isFullBandScan(results[0].getScannedBandsInternal(), true)) { 1088 handleScanResults(mScanDetails); 1089 } 1090 clearScanDetails(); 1091 } 1092 1093 @Override onFullResult(ScanResult fullScanResult)1094 public void onFullResult(ScanResult fullScanResult) { 1095 if (!mWifiEnabled) { 1096 return; 1097 } 1098 mScanDetails.add(new ScanDetail(fullScanResult)); 1099 } 1100 } 1101 } 1102