• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 com.android.server.wifi.DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT;
20 import static com.android.server.wifi.WifiScoreCard.TS_NONE;
21 import static com.android.server.wifi.util.NativeUtil.hexStringFromByteArray;
22 
23 import static org.junit.Assert.*;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.Mockito.*;
26 import static org.mockito.Mockito.when;
27 
28 import android.app.test.MockAnswerUtil.AnswerWithArguments;
29 import android.app.test.TestAlarmManager;
30 import android.content.Context;
31 import android.content.pm.ModuleInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.content.res.Resources;
35 import android.net.MacAddress;
36 import android.net.wifi.ScanResult.InformationElement;
37 import android.net.wifi.WifiConfiguration;
38 import android.net.wifi.WifiScanner;
39 import android.net.wifi.WifiScanner.ScanData;
40 import android.net.wifi.WifiScanner.ScanSettings;
41 import android.net.wifi.WifiSsid;
42 import android.os.Build;
43 import android.os.test.TestLooper;
44 import android.util.LocalLog;
45 
46 import androidx.test.filters.SmallTest;
47 
48 import com.android.dx.mockito.inline.extended.ExtendedMockito;
49 import com.android.server.wifi.ActiveModeWarden.ModeChangeCallback;
50 import com.android.server.wifi.WifiConfigManager.OnNetworkUpdateListener;
51 import com.android.server.wifi.WifiHealthMonitor.ScanStats;
52 import com.android.server.wifi.WifiHealthMonitor.WifiSoftwareBuildInfo;
53 import com.android.server.wifi.WifiHealthMonitor.WifiSystemInfoStats;
54 import com.android.server.wifi.WifiScoreCard.PerNetwork;
55 import com.android.server.wifi.proto.WifiScoreCardProto.SystemInfoStats;
56 import com.android.server.wifi.proto.WifiStatsLog;
57 import com.android.server.wifi.proto.nano.WifiMetricsProto.HealthMonitorMetrics;
58 import com.android.server.wifi.scanner.WifiScannerInternal;
59 import com.android.wifi.resources.R;
60 
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.mockito.ArgumentCaptor;
64 import org.mockito.Mock;
65 import org.mockito.MockitoAnnotations;
66 import org.mockito.MockitoSession;
67 import org.mockito.quality.Strictness;
68 
69 import java.nio.charset.StandardCharsets;
70 import java.util.ArrayList;
71 import java.util.Calendar;
72 import java.util.List;
73 
74 /**
75  * Unit tests for {@link com.android.server.wifi.WifiHealthMonitor}.
76  */
77 @SmallTest
78 public class WifiHealthMonitorTest extends WifiBaseTest {
79 
80     static final WifiSsid TEST_SSID_1 = WifiSsid.fromUtf8Text("Joe's Place");
81     static final WifiSsid TEST_SSID_2 = WifiSsid.fromUtf8Text("Poe's Place");
82     static final MacAddress TEST_BSSID_1 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
83     private static final long CURRENT_ELAPSED_TIME_MS = 1000;
84     private static final String WIFI_IFACE_NAME = "wlanTest";
85 
86     private WifiScoreCard mWifiScoreCard;
87     private WifiHealthMonitor mWifiHealthMonitor;
88     private MockitoSession mSession;
89 
90     @Mock
91     Clock mClock;
92     @Mock
93     WifiScoreCard.MemoryStore mMemoryStore;
94     @Mock
95     WifiInjector mWifiInjector;
96     @Mock
97     Context mContext;
98     @Mock
99     DeviceConfigFacade mDeviceConfigFacade;
100     @Mock
101     WifiNative mWifiNative;
102     @Mock
103     PackageManager mPackageManager;
104     @Mock
105     PackageInfo mPackageInfo;
106     @Mock
107     ModuleInfo mModuleInfo;
108     @Mock
109     FrameworkFacade mFrameworkFacade;
110     @Mock
111     Resources mResources;
112     @Mock
113     ActiveModeWarden mActiveModeWarden;
114     @Mock
115     WifiGlobals mWifiGlobals;
116     @Mock
117     WifiMetrics mWifiMetrics;
118 
119     private final ArrayList<String> mKeys = new ArrayList<>();
120     private final ArrayList<WifiScoreCard.BlobListener> mBlobListeners = new ArrayList<>();
121     private final ArrayList<byte[]> mBlobs = new ArrayList<>();
122 
123     private ScanSettings mScanSettings = new ScanSettings();
124     private WifiConfigManager mWifiConfigManager;
125     private long mMilliSecondsSinceBoot;
126     private ExtendedWifiInfo mWifiInfo;
127     private WifiConfiguration mWifiConfig;
128     private String mDriverVersion;
129     private String mFirmwareVersion;
130     private static final long MODULE_VERSION = 1L;
131     private TestAlarmManager mAlarmManager;
132     private TestLooper mLooper = new TestLooper();
133     private List<WifiConfiguration> mConfiguredNetworks;
134     private WifiScannerInternal mWifiScanner;
135     private ScanData mScanData;
136     private ArgumentCaptor<WifiScannerInternal.ScanListener> mScanListenerArgumentCaptor =
137             ArgumentCaptor.forClass(WifiScannerInternal.ScanListener.class);
138     private OnNetworkUpdateListener mOnNetworkUpdateListener;
139     private ModeChangeCallback mModeChangeCallback;
140 
millisecondsPass(long ms)141     private void millisecondsPass(long ms) {
142         mMilliSecondsSinceBoot += ms;
143         when(mClock.getElapsedSinceBootMillis()).thenReturn(mMilliSecondsSinceBoot);
144         when(mClock.getWallClockMillis()).thenReturn(mMilliSecondsSinceBoot + 1_500_000_000_000L);
145     }
146 
147     /**
148      * Sets up for unit test.
149      */
150     @Before
setUp()151     public void setUp() throws Exception {
152         MockitoAnnotations.initMocks(this);
153         mKeys.clear();
154         mBlobListeners.clear();
155         mBlobs.clear();
156         mConfiguredNetworks = new ArrayList<>();
157         mMilliSecondsSinceBoot = 0;
158         mWifiInfo = new ExtendedWifiInfo(mock(WifiGlobals.class), WIFI_IFACE_NAME);
159         mWifiInfo.setBSSID(TEST_BSSID_1.toString());
160         mWifiInfo.setSSID(TEST_SSID_1);
161         // Add 1st configuration
162         mWifiConfig = new WifiConfiguration();
163         mWifiConfig.SSID = mWifiInfo.getSSID();
164         mConfiguredNetworks.add(mWifiConfig);
165         // Add 2nd configuration
166         mWifiInfo.setSSID(TEST_SSID_2);
167         mWifiConfig = new WifiConfiguration();
168         mWifiConfig.SSID = mWifiInfo.getSSID();
169         mConfiguredNetworks.add(mWifiConfig);
170 
171         millisecondsPass(0);
172 
173         mDriverVersion = "build 1.1";
174         mFirmwareVersion = "HW 1.1";
175         when(mPackageInfo.getLongVersionCode()).thenReturn(MODULE_VERSION);
176         when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
177         when(mPackageManager.getModuleInfo(anyString(), anyInt())).thenReturn(mModuleInfo);
178         when(mModuleInfo.getPackageName()).thenReturn("WifiAPK");
179         when(mContext.getPackageManager()).thenReturn(mPackageManager);
180 
181         mWifiConfigManager = mockConfigManager();
182 
183         mWifiScoreCard = new WifiScoreCard(mClock, "some seed", mDeviceConfigFacade,
184                 mContext, mWifiGlobals);
185         mAlarmManager = new TestAlarmManager();
186         when(mContext.getSystemService(Context.ALARM_SERVICE))
187                 .thenReturn(mAlarmManager.getAlarmManager());
188 
189         mScanData = mockScanData();
190         mWifiScanner = mockWifiScanner(WifiScanner.WIFI_BAND_ALL);
191         when(mWifiNative.getDriverVersion()).thenReturn(mDriverVersion);
192         when(mWifiNative.getFirmwareVersion()).thenReturn(mFirmwareVersion);
193         when(mDeviceConfigFacade.getConnectionFailureHighThrPercent()).thenReturn(
194                 DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_HIGH_THR_PERCENT);
195         when(mDeviceConfigFacade.getConnectionFailureCountMin()).thenReturn(
196                 DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_COUNT_MIN);
197         when(mDeviceConfigFacade.getConnectionFailureDisconnectionHighThrPercent()).thenReturn(
198                 DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_DISCONNECTION_HIGH_THR_PERCENT);
199         when(mDeviceConfigFacade.getConnectionFailureDisconnectionCountMin()).thenReturn(
200                 DeviceConfigFacade.DEFAULT_CONNECTION_FAILURE_DISCONNECTION_COUNT_MIN);
201         when(mDeviceConfigFacade.getAssocRejectionHighThrPercent()).thenReturn(
202                 DeviceConfigFacade.DEFAULT_ASSOC_REJECTION_HIGH_THR_PERCENT);
203         when(mDeviceConfigFacade.getAssocRejectionCountMin()).thenReturn(
204                 DeviceConfigFacade.DEFAULT_ASSOC_REJECTION_COUNT_MIN);
205         when(mDeviceConfigFacade.getAssocTimeoutHighThrPercent()).thenReturn(
206                 DeviceConfigFacade.DEFAULT_ASSOC_TIMEOUT_HIGH_THR_PERCENT);
207         when(mDeviceConfigFacade.getAssocTimeoutCountMin()).thenReturn(
208                 DeviceConfigFacade.DEFAULT_ASSOC_TIMEOUT_COUNT_MIN);
209         when(mDeviceConfigFacade.getAuthFailureHighThrPercent()).thenReturn(
210                 DeviceConfigFacade.DEFAULT_AUTH_FAILURE_HIGH_THR_PERCENT);
211         when(mDeviceConfigFacade.getAuthFailureCountMin()).thenReturn(
212                 DeviceConfigFacade.DEFAULT_AUTH_FAILURE_COUNT_MIN);
213         when(mDeviceConfigFacade.getShortConnectionNonlocalHighThrPercent()).thenReturn(
214                 DeviceConfigFacade.DEFAULT_SHORT_CONNECTION_NONLOCAL_HIGH_THR_PERCENT);
215         when(mDeviceConfigFacade.getShortConnectionNonlocalCountMin()).thenReturn(
216                 DeviceConfigFacade.DEFAULT_SHORT_CONNECTION_NONLOCAL_COUNT_MIN);
217         when(mDeviceConfigFacade.getDisconnectionNonlocalHighThrPercent()).thenReturn(
218                 DeviceConfigFacade.DEFAULT_DISCONNECTION_NONLOCAL_HIGH_THR_PERCENT);
219         when(mDeviceConfigFacade.getDisconnectionNonlocalCountMin()).thenReturn(
220                 DeviceConfigFacade.DEFAULT_DISCONNECTION_NONLOCAL_COUNT_MIN);
221         when(mDeviceConfigFacade.getHealthMonitorMinRssiThrDbm()).thenReturn(
222                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_RSSI_THR_DBM);
223         when(mDeviceConfigFacade.getHealthMonitorRatioThrNumerator()).thenReturn(
224                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_RATIO_THR_NUMERATOR);
225         when(mDeviceConfigFacade.getHealthMonitorMinNumConnectionAttempt()).thenReturn(
226                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT);
227         when(mDeviceConfigFacade.getHealthMonitorShortConnectionDurationThrMs()).thenReturn(
228                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_SHORT_CONNECTION_DURATION_THR_MS);
229         when(mDeviceConfigFacade.getAbnormalDisconnectionReasonCodeMask()).thenReturn(
230                 DeviceConfigFacade.DEFAULT_ABNORMAL_DISCONNECTION_REASON_CODE_MASK);
231         when(mDeviceConfigFacade.getHealthMonitorRssiPollValidTimeMs()).thenReturn(
232                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_RSSI_POLL_VALID_TIME_MS);
233         when(mDeviceConfigFacade.getHealthMonitorFwAlertValidTimeMs()).thenReturn(
234                 DeviceConfigFacade.DEFAULT_HEALTH_MONITOR_FW_ALERT_VALID_TIME_MS);
235         when(mDeviceConfigFacade.getNonstationaryScanRssiValidTimeMs()).thenReturn(
236                 DeviceConfigFacade.DEFAULT_NONSTATIONARY_SCAN_RSSI_VALID_TIME_MS);
237         when(mDeviceConfigFacade.getStationaryScanRssiValidTimeMs()).thenReturn(
238                 DeviceConfigFacade.DEFAULT_STATIONARY_SCAN_RSSI_VALID_TIME_MS);
239         when(mContext.getResources()).thenReturn(mResources);
240         when(mResources.getIntArray(R.array.config_wifiRssiLevelThresholds))
241                 .thenReturn(new int[]{-88, -77, -66, -55});
242         mWifiHealthMonitor = new WifiHealthMonitor(mContext, mWifiInjector, mClock,
243                 mWifiConfigManager, mWifiScoreCard,
244                 new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128), mWifiMetrics),
245                 mWifiNative, "some seed", mDeviceConfigFacade, mActiveModeWarden);
246         mWifiHealthMonitor.enableVerboseLogging(true);
247         mLooper.dispatchAll();
248         ArgumentCaptor<ModeChangeCallback> modeChangeCallbackArgumentCaptor =
249                 ArgumentCaptor.forClass(ModeChangeCallback.class);
250         verify(mActiveModeWarden).registerModeChangeCallback(
251                 modeChangeCallbackArgumentCaptor.capture());
252         mModeChangeCallback = modeChangeCallbackArgumentCaptor.getValue();
253     }
254 
mockConfigManager()255     private WifiConfigManager mockConfigManager() {
256         WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
257         when(wifiConfigManager.getConfiguredNetworks()).thenReturn(mConfiguredNetworks);
258         when(wifiConfigManager.findScanRssi(anyInt(), anyInt()))
259                 .thenReturn(-53);
260 
261         doAnswer(new AnswerWithArguments() {
262             public void answer(OnNetworkUpdateListener listener) throws Exception {
263                 mOnNetworkUpdateListener = listener;
264             }
265         }).when(wifiConfigManager).addOnNetworkUpdateListener(anyObject());
266 
267         doAnswer(new AnswerWithArguments() {
268             public boolean answer(int networkId, int uid, String packageName) throws Exception {
269                 mOnNetworkUpdateListener.onNetworkRemoved(mWifiConfig);
270                 return true;
271             }
272         }).when(wifiConfigManager).removeNetwork(anyInt(), anyInt(), anyString());
273 
274         doAnswer(new AnswerWithArguments() {
275             public NetworkUpdateResult answer(WifiConfiguration config, int uid) throws Exception {
276                 mOnNetworkUpdateListener.onNetworkAdded(config);
277                 return new NetworkUpdateResult(1);
278             }
279         }).when(wifiConfigManager).addOrUpdateNetwork(any(), anyInt());
280 
281         return wifiConfigManager;
282     }
283 
mockScanData()284     ScanData mockScanData() {
285         ScanData[] scanDatas =
286                 ScanTestUtil.createScanDatas(new int[][]{{5150, 5175, 2412, 2437}}, new int[]{0});
287         // Scan result does require to have an IE.
288         scanDatas[0].getResults()[0].informationElements = new InformationElement[0];
289         scanDatas[0].getResults()[1].informationElements = new InformationElement[0];
290         scanDatas[0].getResults()[2].informationElements = new InformationElement[0];
291         scanDatas[0].getResults()[3].informationElements = new InformationElement[0];
292 
293         return scanDatas[0];
294     }
295 
mockScanDataAbove2GOnly()296     ScanData mockScanDataAbove2GOnly() {
297         ScanData[] scanDatas =
298                 ScanTestUtil.createScanDatas(new int[][]{{5150, 5175, 5500, 5845}}, new int[]{0});
299         // Scan result does require to have an IE.
300         scanDatas[0].getResults()[0].informationElements = new InformationElement[0];
301         scanDatas[0].getResults()[1].informationElements = new InformationElement[0];
302         scanDatas[0].getResults()[2].informationElements = new InformationElement[0];
303         scanDatas[0].getResults()[3].informationElements = new InformationElement[0];
304 
305         return scanDatas[0];
306     }
307 
mockWifiScanner(@ifiScanner.WifiBand int wifiBand)308     WifiScannerInternal mockWifiScanner(@WifiScanner.WifiBand int wifiBand) {
309         WifiScannerInternal scanner = mock(WifiScannerInternal.class);
310 
311         doNothing().when(scanner).registerScanListener(mScanListenerArgumentCaptor.capture());
312         ScanData[] scanDatas = new ScanData[1];
313         scanDatas[0] = mock(ScanData.class);
314         when(scanDatas[0].getScannedBandsInternal()).thenReturn(wifiBand);
315         doAnswer(new AnswerWithArguments() {
316             public void answer(ScanSettings settings, WifiScannerInternal.ScanListener listener)
317                     throws Exception {
318                 if (mScanData != null && mScanData.getResults() != null) {
319                     for (int i = 0; i < mScanData.getResults().length; i++) {
320                         listener.onFullResult(
321                                 mScanData.getResults()[i]);
322                     }
323                 }
324                 listener.onResults(scanDatas);
325             }
326         }).when(scanner).startScan(anyObject(), anyObject());
327         WifiLocalServices.removeServiceForTest(WifiScannerInternal.class);
328         WifiLocalServices.addService(WifiScannerInternal.class, scanner);
329         return scanner;
330     }
331 
332 
makeNetworkConnectionExample()333     private void makeNetworkConnectionExample() {
334         mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
335         millisecondsPass(5000);
336         mWifiInfo.setRssi(-55);
337         mWifiScoreCard.noteIpConfiguration(mWifiInfo);
338         mWifiScoreCard.noteValidationSuccess(mWifiInfo);
339         millisecondsPass(1000);
340         mWifiScoreCard.noteSignalPoll(mWifiInfo);
341         millisecondsPass(2000);
342         int disconnectionReason = 0;
343         mWifiScoreCard.noteNonlocalDisconnect(WIFI_IFACE_NAME, disconnectionReason);
344         millisecondsPass(10);
345         mWifiScoreCard.resetAllConnectionStates();
346     }
347 
makeRecentStatsWithSufficientConnectionAttempt()348     private void makeRecentStatsWithSufficientConnectionAttempt() {
349         for (int i = 0; i < DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT; i++) {
350             makeNetworkConnectionExample();
351         }
352     }
353 
setWifiEnabled(boolean enabled)354     private void setWifiEnabled(boolean enabled) {
355         if (enabled) {
356             when(mActiveModeWarden.getPrimaryClientModeManagerNullable())
357                     .thenReturn(mock(ConcreteClientModeManager.class));
358             mModeChangeCallback.onActiveModeManagerAdded(mock(ConcreteClientModeManager.class));
359         } else {
360             when(mActiveModeWarden.getPrimaryClientModeManagerNullable()).thenReturn(null);
361             mModeChangeCallback.onActiveModeManagerRemoved(mock(ConcreteClientModeManager.class));
362         }
363     }
364 
makeSerializedExample()365     private byte[] makeSerializedExample() {
366         // Install a placeholder memoryStore
367         // trigger extractCurrentSoftwareBuildInfo() call to update currSoftwareBuildInfo
368         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
369         setWifiEnabled(true);
370 
371         assertEquals(MODULE_VERSION, mWifiHealthMonitor.getWifiStackVersion());
372         millisecondsPass(5000);
373         mWifiScanner.startScan(mScanSettings, mScanListenerArgumentCaptor.getValue());
374         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
375         mLooper.dispatchAll();
376         // serialized now has currSoftwareBuildInfo and scan results
377         return mWifiHealthMonitor.getWifiSystemInfoStats().toSystemInfoStats().toByteArray();
378     }
379 
makeSwBuildChangeExample(String firmwareVersion)380     private void makeSwBuildChangeExample(String firmwareVersion) {
381         byte[] serialized = makeSerializedExample();
382         // Install a real MemoryStore object, which records read requests
383         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(new WifiScoreCard.MemoryStore() {
384             @Override
385             public void read(String key, String name, WifiScoreCard.BlobListener listener) {
386                 mBlobListeners.add(listener);
387             }
388 
389             @Override
390             public void write(String key, String name, byte[] value) {
391                 mKeys.add(key);
392                 mBlobs.add(value);
393             }
394 
395             @Override
396             public void setCluster(String key, String cluster) {
397             }
398 
399             @Override
400             public void removeCluster(String cluster) {
401             }
402         });
403         mBlobListeners.get(0).onBlobRetrieved(serialized);
404 
405         // Change current FW version
406         when(mWifiNative.getFirmwareVersion()).thenReturn(firmwareVersion);
407     }
408 
409     /**
410      * Test read and write around SW change.
411      */
412     @Test
testReadWriteAndSWChange()413     public void testReadWriteAndSWChange() throws Exception {
414         String firmwareVersion = "HW 1.2";
415         makeSwBuildChangeExample(firmwareVersion);
416         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
417         mLooper.dispatchAll();
418         // Now it should detect SW change, disable WiFi to trigger write
419         setWifiEnabled(false);
420 
421         // Check current and previous FW version of WifiSystemInfoStats
422         WifiSystemInfoStats wifiSystemInfoStats = mWifiHealthMonitor.getWifiSystemInfoStats();
423         assertEquals(firmwareVersion, wifiSystemInfoStats.getCurrSoftwareBuildInfo()
424                 .getWifiFirmwareVersion());
425         assertEquals(mFirmwareVersion, wifiSystemInfoStats.getPrevSoftwareBuildInfo()
426                 .getWifiFirmwareVersion());
427         assertEquals(MODULE_VERSION, mWifiHealthMonitor.getWifiStackVersion());
428 
429         // Check write
430         String writtenHex = hexStringFromByteArray(mBlobs.get(mKeys.size() - 1));
431         String currFirmwareVersionHex = hexStringFromByteArray(
432                 firmwareVersion.getBytes(StandardCharsets.UTF_8));
433         String prevFirmwareVersionHex = hexStringFromByteArray(
434                 mFirmwareVersion.getBytes(StandardCharsets.UTF_8));
435         assertTrue(writtenHex, writtenHex.contains(currFirmwareVersionHex));
436         assertTrue(writtenHex, writtenHex.contains(prevFirmwareVersionHex));
437     }
438 
439     /**
440      * Test serialization and deserialization of WifiSystemInfoStats.
441      */
442     @Test
testSerializationDeserialization()443     public void testSerializationDeserialization() throws Exception  {
444         // Install a placeholder memoryStore
445         // trigger extractCurrentSoftwareBuildInfo() call to update currSoftwareBuildInfo
446         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
447         setWifiEnabled(true);
448         millisecondsPass(5000);
449         mWifiScanner.startScan(mScanSettings, mScanListenerArgumentCaptor.getValue());
450         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
451         mLooper.dispatchAll();
452         WifiSystemInfoStats wifiSystemInfoStats = mWifiHealthMonitor.getWifiSystemInfoStats();
453         // serialized now has currSoftwareBuildInfo and recent scan info
454         byte[] serialized = wifiSystemInfoStats.toSystemInfoStats().toByteArray();
455         SystemInfoStats systemInfoStats = SystemInfoStats.parseFrom(serialized);
456         WifiSoftwareBuildInfo currSoftwareBuildInfoFromMemory = wifiSystemInfoStats
457                 .fromSoftwareBuildInfo(systemInfoStats.getCurrSoftwareBuildInfo());
458         assertEquals(MODULE_VERSION, currSoftwareBuildInfoFromMemory.getWifiStackVersion());
459         assertEquals(mDriverVersion, currSoftwareBuildInfoFromMemory.getWifiDriverVersion());
460         assertEquals(mFirmwareVersion, currSoftwareBuildInfoFromMemory.getWifiFirmwareVersion());
461         assertEquals(Build.DISPLAY, currSoftwareBuildInfoFromMemory.getOsBuildVersion());
462         assertEquals(1_500_000_005_000L, systemInfoStats.getLastScanTimeMs());
463         assertEquals(2, systemInfoStats.getNumBssidLastScan2G());
464         assertEquals(2, systemInfoStats.getNumBssidLastScanAbove2G());
465     }
466 
467     /**
468      * Check alarm timing of a multi-day run.
469      */
470     @Test
testTimerMultiDayRun()471     public void testTimerMultiDayRun() throws Exception {
472         long currentWallClockTimeMs = 23 * 3600_000;
473         long currentElapsedTimeMs = CURRENT_ELAPSED_TIME_MS;
474         Calendar calendar = Calendar.getInstance();
475         calendar.setTimeInMillis(currentWallClockTimeMs);
476         int expectedWaitHours = WifiHealthMonitor.DAILY_DETECTION_HOUR
477                 - calendar.get(Calendar.HOUR_OF_DAY);
478         if (expectedWaitHours <= 0) expectedWaitHours += 24;
479 
480         // day 1
481         when(mClock.getElapsedSinceBootMillis()).thenReturn(currentElapsedTimeMs);
482         when(mClock.getWallClockMillis()).thenReturn(currentWallClockTimeMs);
483         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
484         long waitTimeMs = mAlarmManager
485                 .getTriggerTimeMillis(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG)
486                 - currentElapsedTimeMs;
487         assertEquals(expectedWaitHours * 3600_000, waitTimeMs);
488         currentElapsedTimeMs += 24 * 3600_000 + 1;
489         when(mClock.getElapsedSinceBootMillis()).thenReturn(currentElapsedTimeMs);
490         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
491         mLooper.dispatchAll();
492         waitTimeMs = mAlarmManager
493                 .getTriggerTimeMillis(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG)
494                 - currentElapsedTimeMs;
495         assertEquals(24 * 3600_000, waitTimeMs);
496         // day 2
497         currentElapsedTimeMs += 24 * 3600_000 - 1;
498         when(mClock.getElapsedSinceBootMillis()).thenReturn(currentElapsedTimeMs);
499         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
500         mLooper.dispatchAll();
501         waitTimeMs = mAlarmManager
502                 .getTriggerTimeMillis(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG)
503                 - currentElapsedTimeMs;
504         assertEquals(24 * 3600_000, waitTimeMs);
505     }
506 
507     /**
508      * Check the alarm timing with a different wall clock time
509      */
510     @Test
testTimerWith()511     public void testTimerWith() throws Exception {
512         long currentWallClockTimeMs = 7 * 3600_000;
513         long currentElapsedTimeMs = CURRENT_ELAPSED_TIME_MS;
514         Calendar calendar = Calendar.getInstance();
515         calendar.setTimeInMillis(currentWallClockTimeMs);
516         int expectedWaitHours = WifiHealthMonitor.DAILY_DETECTION_HOUR
517                 - calendar.get(Calendar.HOUR_OF_DAY);
518         if (expectedWaitHours <= 0) expectedWaitHours += 24;
519 
520         // day 1
521         when(mClock.getElapsedSinceBootMillis()).thenReturn(currentElapsedTimeMs);
522         when(mClock.getWallClockMillis()).thenReturn(currentWallClockTimeMs);
523         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
524         long waitTimeMs = mAlarmManager
525                 .getTriggerTimeMillis(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG)
526                 - currentElapsedTimeMs;
527         assertEquals(expectedWaitHours * 3600_000, waitTimeMs);
528     }
529 
530     /**
531      * Check stats with two daily detections.
532      */
533     @Test
testTwoDailyDetections()534     public void testTwoDailyDetections() throws Exception {
535         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
536         // day 1
537         makeRecentStatsWithSufficientConnectionAttempt();
538         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
539         mLooper.dispatchAll();
540         // day 2
541         makeRecentStatsWithSufficientConnectionAttempt();
542         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
543         mLooper.dispatchAll();
544 
545         PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
546         assertEquals(DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT * 2,
547                 perNetwork.getStatsCurrBuild().getCount(WifiScoreCard.CNT_CONNECTION_ATTEMPT));
548     }
549 
550     /**
551      * Check proto after one daily detection with high non-local disconnection rate
552      */
553     @Test
testBuildProto()554     public void testBuildProto() throws Exception {
555         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
556         makeRecentStatsWithSufficientConnectionAttempt();
557         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
558         mLooper.dispatchAll();
559 
560         // First call of buildProto
561         HealthMonitorMetrics healthMetrics = mWifiHealthMonitor.buildProto();
562         assertEquals(0, healthMetrics.failureStatsIncrease.cntAssocRejection);
563         assertEquals(0, healthMetrics.failureStatsIncrease.cntAssocTimeout);
564         assertEquals(0, healthMetrics.failureStatsIncrease.cntAuthFailure);
565         assertEquals(0, healthMetrics.failureStatsIncrease.cntConnectionFailure);
566         assertEquals(0, healthMetrics.failureStatsIncrease.cntDisconnectionNonlocalConnecting);
567         assertEquals(0, healthMetrics.failureStatsIncrease.cntDisconnectionNonlocal);
568         assertEquals(0, healthMetrics.failureStatsIncrease.cntShortConnectionNonlocal);
569         assertEquals(0, healthMetrics.failureStatsHigh.cntAssocRejection);
570         assertEquals(0, healthMetrics.failureStatsHigh.cntAssocTimeout);
571         assertEquals(0, healthMetrics.failureStatsHigh.cntAuthFailure);
572         assertEquals(0, healthMetrics.failureStatsHigh.cntConnectionFailure);
573         assertEquals(1, healthMetrics.failureStatsHigh.cntDisconnectionNonlocal);
574         assertEquals(1, healthMetrics.failureStatsHigh.cntShortConnectionNonlocal);
575         assertEquals(1, healthMetrics.numNetworkSufficientRecentStatsOnly);
576         assertEquals(0, healthMetrics.numNetworkSufficientRecentPrevStats);
577 
578         // Second call of buildProto
579         healthMetrics = mWifiHealthMonitor.buildProto();
580         // Second call should result in an empty proto
581         assertEquals(null, healthMetrics);
582     }
583 
584     /**
585      * Test FailureStats class
586      */
587     @Test
testFailureStats()588     public void testFailureStats() throws Exception {
589         WifiHealthMonitor.FailureStats failureStats = new WifiHealthMonitor.FailureStats();
590         failureStats.setCount(WifiHealthMonitor.REASON_AUTH_FAILURE, 10);
591         failureStats.incrementCount(WifiHealthMonitor.REASON_AUTH_FAILURE);
592 
593         String expectedString = "authentication failure: 11 ";
594         String unexpectedString =
595                 WifiHealthMonitor.FAILURE_REASON_NAME[WifiHealthMonitor.REASON_ASSOC_REJECTION];
596         assertEquals(11, failureStats.getCount(WifiHealthMonitor.REASON_AUTH_FAILURE));
597         assertEquals(true, failureStats.toString().contains(expectedString));
598         assertEquals(false, failureStats.toString().contains(unexpectedString));
599     }
600 
601     /**
602      * Check statsd logging after one daily detection with high non-local disconnection rate
603      */
604     @Test
testWifiStatsLogWrite()605     public void testWifiStatsLogWrite() throws Exception {
606         // static mocking for WifiStatsLog
607         mSession = ExtendedMockito.mockitoSession()
608                 .strictness(Strictness.LENIENT)
609                 .mockStatic(WifiStatsLog.class)
610                 .startMocking();
611 
612         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
613         makeRecentStatsWithSufficientConnectionAttempt();
614         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
615         mLooper.dispatchAll();
616 
617         ExtendedMockito.verify(() -> WifiStatsLog.write(
618                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED,
619                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__ABNORMALITY_TYPE__SIMPLY_HIGH,
620                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_NON_LOCAL_DISCONNECTION,
621                 1));
622 
623         ExtendedMockito.verify(() -> WifiStatsLog.write(
624                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED,
625                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__ABNORMALITY_TYPE__SIMPLY_HIGH,
626                 WifiStatsLog.WIFI_FAILURE_STAT_REPORTED__FAILURE_TYPE__FAILURE_SHORT_CONNECTION_DUE_TO_NON_LOCAL_DISCONNECTION,
627                 1));
628         mSession.finishMocking();
629     }
630 
631     /**
632      * test stats after a SW build change
633      */
634     @Test
testAfterSwBuildChange()635     public void testAfterSwBuildChange() throws Exception {
636         // Day 1
637         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
638         makeRecentStatsWithSufficientConnectionAttempt();
639         mAlarmManager.dispatch(WifiHealthMonitor.DAILY_DETECTION_TIMER_TAG);
640         mLooper.dispatchAll();
641 
642         // Day 2
643         String firmwareVersion = "HW 1.2";
644         makeSwBuildChangeExample(firmwareVersion);
645         // Disable WiFi before post-boot-detection
646         setWifiEnabled(false);
647 
648         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
649         mLooper.dispatchAll();
650         // Skip SW build change detection
651         PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
652         assertEquals(DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT * 1,
653                 perNetwork.getStatsCurrBuild().getCount(WifiScoreCard.CNT_CONNECTION_ATTEMPT));
654         assertEquals(DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT * 0,
655                 perNetwork.getStatsPrevBuild().getCount(WifiScoreCard.CNT_CONNECTION_ATTEMPT));
656 
657         // Day 3
658         setWifiEnabled(true);
659         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
660         mLooper.dispatchAll();
661         // Finally detect SW build change
662         assertEquals(0,
663                 perNetwork.getStatsCurrBuild().getCount(WifiScoreCard.CNT_CONNECTION_ATTEMPT));
664         assertEquals(DEFAULT_HEALTH_MONITOR_MIN_NUM_CONNECTION_ATTEMPT * 1,
665                 perNetwork.getStatsPrevBuild().getCount(WifiScoreCard.CNT_CONNECTION_ATTEMPT));
666     }
667 
668     /**
669      * Installing a MemoryStore after startup should issue reads.
670      */
671     @Test
testReadAfterDelayedMemoryStoreInstallation()672     public void testReadAfterDelayedMemoryStoreInstallation() throws Exception {
673         makeNetworkConnectionExample();
674         assertEquals(2, mConfiguredNetworks.size());
675         mWifiScoreCard.installMemoryStore(mMemoryStore);
676         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
677 
678         // 1 for WifiSystemInfoStats, 1 for requestReadBssid and 2 for requestReadNetwork
679         verify(mMemoryStore, times(4)).read(any(), any(), any());
680     }
681 
682     /**
683      * Installing a MemoryStore during startup should issue a proper number of reads.
684      */
685     @Test
testReadAfterStartupMemoryStoreInstallation()686     public void testReadAfterStartupMemoryStoreInstallation() throws Exception {
687         mWifiScoreCard.installMemoryStore(mMemoryStore);
688         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
689         makeNetworkConnectionExample();
690         assertEquals(2, mConfiguredNetworks.size());
691 
692         // 1 for WifiSystemInfoStats, 1 for requestReadBssid and 2 for requestReadNetwork
693         verify(mMemoryStore, times(4)).read(any(), any(), any());
694     }
695 
696     /**
697      * Installing a MemoryStore twice should not cause crash.
698      */
699     @Test
testInstallMemoryStoreTwiceNoCrash()700     public void testInstallMemoryStoreTwiceNoCrash() throws Exception {
701         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
702         makeNetworkConnectionExample();
703         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(mMemoryStore);
704     }
705 
706     /**
707      * Check if scan results are reported correctly after full band scan.
708      */
709     @Test
testFullBandScan()710     public void testFullBandScan() throws Exception {
711         millisecondsPass(5000);
712         setWifiEnabled(true);
713         mWifiScanner.startScan(mScanSettings, mScanListenerArgumentCaptor.getValue());
714         mLooper.dispatchAll();
715         ScanStats scanStats = mWifiHealthMonitor.getWifiSystemInfoStats().getCurrScanStats();
716         assertEquals(1_500_000_005_000L, scanStats.getLastScanTimeMs());
717         assertEquals(2, scanStats.getNumBssidLastScanAbove2g());
718         assertEquals(2, scanStats.getNumBssidLastScan2g());
719     }
720 
721     /**
722      * Check if scan results are reported correctly after 2G only scan.
723      */
724     @Test
test2GScan()725     public void test2GScan() throws Exception {
726         mWifiScanner = mockWifiScanner(WifiScanner.WIFI_BAND_24_GHZ);
727         millisecondsPass(5000);
728         setWifiEnabled(true);
729         mWifiScanner.startScan(mScanSettings, mScanListenerArgumentCaptor.getValue());
730         ScanStats scanStats = mWifiHealthMonitor.getWifiSystemInfoStats().getCurrScanStats();
731         assertEquals(TS_NONE, scanStats.getLastScanTimeMs());
732         assertEquals(0, scanStats.getNumBssidLastScanAbove2g());
733         assertEquals(0, scanStats.getNumBssidLastScan2g());
734     }
735 
736     @Test
testClearReallyDoesClearTheState()737     public void testClearReallyDoesClearTheState() throws Exception {
738         byte[] init = mWifiHealthMonitor.getWifiSystemInfoStats()
739                 .toSystemInfoStats().toByteArray();
740         byte[] serialized = makeSerializedExample();
741         assertNotEquals(0, serialized.length);
742         mWifiHealthMonitor.clear();
743         byte[] leftovers = mWifiHealthMonitor.getWifiSystemInfoStats()
744                 .toSystemInfoStats().toByteArray();
745         assertEquals(init.length, leftovers.length);
746     }
747 
748     @Test
testPostBootAbnormalScanDetection()749     public void testPostBootAbnormalScanDetection() throws Exception {
750         // Serialized has the last scan result
751         byte [] serialized = makeSerializedExample();
752         // Startup DUT again to mimic reboot
753         setUp();
754         // Install a real MemoryStore object, which records read requests
755         mWifiHealthMonitor.installMemoryStoreSetUpDetectionAlarm(new WifiScoreCard.MemoryStore() {
756             @Override
757             public void read(String key, String name, WifiScoreCard.BlobListener listener) {
758                 mBlobListeners.add(listener);
759             }
760 
761             @Override
762             public void write(String key, String name, byte[] value) {
763                 mKeys.add(key);
764                 mBlobs.add(value);
765             }
766 
767             @Override
768             public void setCluster(String key, String cluster) {
769             }
770 
771             @Override
772             public void removeCluster(String cluster) {
773             }
774         });
775         mBlobListeners.get(0).onBlobRetrieved(serialized);
776 
777         SystemInfoStats systemInfoStats = SystemInfoStats.parseFrom(serialized);
778         assertEquals(1_500_000_005_000L, systemInfoStats.getLastScanTimeMs());
779         assertEquals(2, systemInfoStats.getNumBssidLastScan2G());
780         assertEquals(2, systemInfoStats.getNumBssidLastScanAbove2G());
781 
782         // Add Above2G only scan data
783         mScanData = mockScanDataAbove2GOnly();
784         mWifiScanner = mockWifiScanner(WifiScanner.WIFI_BAND_ALL);
785 
786         millisecondsPass(5000);
787         setWifiEnabled(true);
788         mWifiScanner.startScan(mScanSettings, mScanListenerArgumentCaptor.getValue());
789 
790         mAlarmManager.dispatch(WifiHealthMonitor.POST_BOOT_DETECTION_TIMER_TAG);
791         mLooper.dispatchAll();
792 
793         // It should detect abnormal scan failure now.
794         assertEquals(4, mWifiHealthMonitor.getWifiSystemInfoStats().getScanFailure());
795     }
796 
797     /**
798      * Test when remove a saved network will remove network from the WifiScoreCard.
799      */
800     @Test
testRemoveSavedNetwork()801     public void testRemoveSavedNetwork() {
802         makeNetworkConnectionExample();
803         PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
804         assertNotNull(perNetwork);
805 
806         // Now remove the network
807         mWifiConfigManager.removeNetwork(1, 1, "some package");
808         perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
809         assertNull(perNetwork);
810     }
811 
812     /**
813      * Test when remove a suggestion network will not remove network from the WifiScoreCard.
814      */
815     @Test
testRemoveSuggestionNetwork()816     public void testRemoveSuggestionNetwork() throws Exception {
817         mWifiConfig.fromWifiNetworkSuggestion = true;
818         makeNetworkConnectionExample();
819         PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
820         assertNotNull(perNetwork);
821 
822         // Now remove the network
823         mWifiConfigManager.removeNetwork(1, 1, "some package");
824         perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
825         assertNotNull(perNetwork);
826     }
827 
828     @Test
testAddNetwork()829     public void testAddNetwork() throws Exception {
830         PerNetwork perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
831         assertNull(perNetwork);
832 
833         // Now add network
834         mWifiConfigManager.addOrUpdateNetwork(mWifiConfig, 1);
835         perNetwork = mWifiScoreCard.fetchByNetwork(mWifiInfo.getSSID());
836         assertNotNull(perNetwork);
837     }
838 }
839