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