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