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