/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.WifiInfo; import android.os.Looper; import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.Settings; import androidx.test.filters.SmallTest; import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Unit tests for {@link com.android.server.wifi.WifiDataStall}. */ @SmallTest public class WifiDataStallTest { @Mock Context mContext; @Mock FrameworkFacade mFacade; @Mock WifiMetrics mWifiMetrics; WifiDataStall mWifiDataStall; @Mock Clock mClock; @Mock DeviceConfigFacade mDeviceConfigFacade; @Mock Looper mClientModeImplLooper; @Mock WifiInfo mWifiInfo; private final WifiLinkLayerStats mOldLlStats = new WifiLinkLayerStats(); private final WifiLinkLayerStats mNewLlStats = new WifiLinkLayerStats(); final ArgumentCaptor mOnPropertiesChangedListenerCaptor = ArgumentCaptor.forClass(OnPropertiesChangedListener.class); /** * Sets up for unit test */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mFacade.getIntegerSetting(mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD, WifiDataStall.MIN_TX_BAD_DEFAULT)) .thenReturn(WifiDataStall.MIN_TX_BAD_DEFAULT); when(mFacade.getIntegerSetting(mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX, WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT)) .thenReturn(WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); when(mDeviceConfigFacade.getDataStallTxTputThrMbps()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS); when(mDeviceConfigFacade.getDataStallRxTputThrMbps()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS); when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR); when(mDeviceConfigFacade.getDataStallCcaLevelThr()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_CCA_LEVEL_THR); when(mWifiInfo.getLinkSpeed()).thenReturn(100); when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(100); when(mWifiInfo.getFrequency()).thenReturn(5850); when(mWifiInfo.getBSSID()).thenReturn("5G_WiFi"); mWifiDataStall = new WifiDataStall(mContext, mFacade, mWifiMetrics, mDeviceConfigFacade, mClientModeImplLooper, mClock); mOldLlStats.txmpdu_be = 1000; mOldLlStats.retries_be = 1000; mOldLlStats.lostmpdu_be = 3000; mOldLlStats.rxmpdu_be = 4000; mOldLlStats.timeStampInMs = 10000; mNewLlStats.txmpdu_be = 2 * mOldLlStats.txmpdu_be; mNewLlStats.retries_be = 10 * mOldLlStats.retries_be; mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be; mNewLlStats.rxmpdu_be = mOldLlStats.rxmpdu_be; mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL - 1; verify(mDeviceConfigFacade).addOnPropertiesChangedListener(any(), mOnPropertiesChangedListenerCaptor.capture()); } /** * Verify that LinkLayerStats for WifiIsUnusableEvent is correctly updated */ private void verifyUpdateWifiIsUnusableLinkLayerStats() { verify(mWifiMetrics).updateWifiIsUnusableLinkLayerStats( mNewLlStats.txmpdu_be - mOldLlStats.txmpdu_be, mNewLlStats.retries_be - mOldLlStats.retries_be, mNewLlStats.lostmpdu_be - mOldLlStats.lostmpdu_be, mNewLlStats.rxmpdu_be - mOldLlStats.rxmpdu_be, mNewLlStats.timeStampInMs - mOldLlStats.timeStampInMs); } /** * Verify there is data stall from tx failures */ @Test public void verifyDataStallTxFailure() throws Exception { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics).logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); } /** * Verify there is no data stall from tx failures if tx failures are not consecutively bad */ @Test public void verifyNoDataStallWhenTxFailureIsNotConsecutive() throws Exception { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); mNewLlStats.retries_be = 2 * mOldLlStats.retries_be; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics, never()).logWifiIsUnusableEvent( WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); } /** * Verify there is data stall from rx failures */ @Test public void verifyDataStallRxFailure() throws Exception { when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(1); mNewLlStats.retries_be = 2 * mOldLlStats.retries_be; when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics).logWifiIsUnusableEvent( WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX); } /** * Verify there is data stall from both tx and rx failures */ @Test public void verifyDataStallBothTxRxFailure() throws Exception { when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(1); when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics).logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH); } /** * Verify that we can change the duration of evaluating Wifi conditions * to trigger data stall from DeviceConfigFacade */ @Test public void verifyDataStallDurationDeviceConfigChange() throws Exception { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); when(mDeviceConfigFacade.getDataStallDurationMs()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS + 1); mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics, never()).logWifiIsUnusableEvent( WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); } /** * Verify that we can change the threshold of Tx packet error rate to trigger a data stall * from DeviceConfigFacade */ @Test public void verifyDataStallTxPerThrDeviceConfigChange() throws Exception { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); when(mDeviceConfigFacade.getDataStallTxPerThr()).thenReturn( DeviceConfigFacade.DEFAULT_DATA_STALL_TX_PER_THR + 1); mOnPropertiesChangedListenerCaptor.getValue().onPropertiesChanged(null); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); when(mClock.getElapsedSinceBootMillis()).thenReturn( 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics, never()).logWifiIsUnusableEvent( WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); } /** * Verify there is no data stall when there are no new tx/rx failures */ @Test public void verifyNoDataStallWhenNoFail() throws Exception { assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics, never()).resetWifiIsUnusableLinkLayerStats(); verifyUpdateWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); } /** * Verify there is no data stall when the time difference between * two WifiLinkLayerStats is too big. */ @Test public void verifyNoDataStallBigTimeGap() throws Exception { mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL + 1; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); } /** * Verify that metrics get reset when there is a reset in WifiLinkLayerStats */ @Test public void verifyReset() throws Exception { mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be - 1; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics).resetWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).updateWifiIsUnusableLinkLayerStats( anyLong(), anyLong(), anyLong(), anyLong(), anyLong()); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); } }