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