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