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