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