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