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