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