diff options
7 files changed, 408 insertions, 77 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 8e8361ed4..b80371285 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -765,6 +765,11 @@ public class ClientModeImpl extends StateMachine { private final WrongPasswordNotifier mWrongPasswordNotifier; private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; private boolean mConnectedMacRandomzationSupported; + // Maximum duration to continue to log Wifi usability stats after a data stall is triggered. + @VisibleForTesting + public static final long DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS = 30 * 1000; + private long mDataStallTriggerTimeMs = -1; + private int mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN; public ClientModeImpl(Context context, FrameworkFacade facade, Looper looper, UserManager userManager, WifiInjector wifiInjector, @@ -5109,11 +5114,24 @@ public class ClientModeImpl extends StateMachine { } mWifiScoreReport.noteIpCheck(); } - int statusDataStall = - mWifiDataStall.checkForDataStall(mLastLinkLayerStats, stats); - if (statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) { - mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, - convertToUsabilityStatsTriggerType(statusDataStall), -1); + int statusDataStall = mWifiDataStall.checkForDataStall( + mLastLinkLayerStats, stats, mWifiInfo); + if (mDataStallTriggerTimeMs == -1 + && statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) { + mDataStallTriggerTimeMs = mClock.getElapsedSinceBootMillis(); + mLastStatusDataStall = statusDataStall; + } + if (mDataStallTriggerTimeMs != -1) { + long elapsedTime = mClock.getElapsedSinceBootMillis() + - mDataStallTriggerTimeMs; + if (elapsedTime >= DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS) { + mDataStallTriggerTimeMs = -1; + mWifiMetrics.addToWifiUsabilityStatsList( + WifiUsabilityStats.LABEL_BAD, + convertToUsabilityStatsTriggerType(mLastStatusDataStall), + -1); + mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN; + } } mWifiMetrics.incrementWifiLinkLayerUsageStats(stats); mLastLinkLayerStats = stats; diff --git a/service/java/com/android/server/wifi/DeviceConfigFacade.java b/service/java/com/android/server/wifi/DeviceConfigFacade.java index c64cd5259..a9889f424 100644 --- a/service/java/com/android/server/wifi/DeviceConfigFacade.java +++ b/service/java/com/android/server/wifi/DeviceConfigFacade.java @@ -28,6 +28,17 @@ public class DeviceConfigFacade { private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = (int) TimeUnit.SECONDS.toMillis(30); private static final String NAMESPACE = "wifi"; + // Default duration for evaluating Wifi condition to trigger a data stall + // measured in milliseconds + public static final int DEFAULT_DATA_STALL_DURATION_MS = 1500; + // Default threshold of Tx throughput below which to trigger a data stall measured in Mbps + public static final int DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS = 2; + // Default threshold of Rx throughput below which to trigger a data stall measured in Mbps + public static final int DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS = 2; + // Default threshold of Tx packet error rate above which to trigger a data stall in percentage + public static final int DEFAULT_DATA_STALL_TX_PER_THR = 90; + // Default threshold of CCA level above which to trigger a data stall in percentage + public static final int DEFAULT_DATA_STALL_CCA_LEVEL_THR = 100; /** * Gets the feature flag for reporting abnormally long connections. @@ -54,4 +65,44 @@ public class DeviceConfigFacade { DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor, onPropertiesChangedListener); } + + /** + * Gets the duration of evaluating Wifi condition to trigger a data stall. + */ + public int getDataStallDurationMs() { + return DeviceConfig.getInt(NAMESPACE, "data_stall_duration_ms", + DEFAULT_DATA_STALL_DURATION_MS); + } + + /** + * Gets the threshold of Tx throughput below which to trigger a data stall. + */ + public int getDataStallTxTputThrMbps() { + return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_tput_thr_mbps", + DEFAULT_DATA_STALL_TX_TPUT_THR_MBPS); + } + + /** + * Gets the threshold of Rx throughput below which to trigger a data stall. + */ + public int getDataStallRxTputThrMbps() { + return DeviceConfig.getInt(NAMESPACE, "data_stall_rx_tput_thr_mbps", + DEFAULT_DATA_STALL_RX_TPUT_THR_MBPS); + } + + /** + * Gets the threshold of Tx packet error rate above which to trigger a data stall. + */ + public int getDataStallTxPerThr() { + return DeviceConfig.getInt(NAMESPACE, "data_stall_tx_per_thr", + DEFAULT_DATA_STALL_TX_PER_THR); + } + + /** + * Gets the threshold of CCA level above which to trigger a data stall. + */ + public int getDataStallCcaLevelThr() { + return DeviceConfig.getInt(NAMESPACE, "data_stall_cca_level_thr", + DEFAULT_DATA_STALL_CCA_LEVEL_THR); + } } diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java index 1054c2ef1..6eb3b41e5 100644 --- a/service/java/com/android/server/wifi/WifiDataStall.java +++ b/service/java/com/android/server/wifi/WifiDataStall.java @@ -17,6 +17,9 @@ package com.android.server.wifi; import android.content.Context; +import android.net.wifi.WifiInfo; +import android.os.Handler; +import android.os.Looper; import android.provider.Settings; import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; @@ -33,19 +36,50 @@ public class WifiDataStall { public static final int MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT = 50; // Maximum time gap between two WifiLinkLayerStats to trigger a data stall public static final long MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute + // Maximum time that a data stall start time stays valid. + public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes + // Default Tx packet error rate when there is no Tx attempt + public static final int DEFAULT_TX_PACKET_ERROR_RATE = 20; + // Default CCA level when CCA stats are not available + public static final int DEFAULT_CCA_LEVEL = 0; private final Context mContext; + private final DeviceConfigFacade mDeviceConfigFacade; private final FrameworkFacade mFacade; private final WifiMetrics mWifiMetrics; + private Handler mHandler; private int mMinTxBad; private int mMinTxSuccessWithoutRx; + private int mDataStallDurationMs; + private int mDataStallTxTputThrMbps; + private int mDataStallRxTputThrMbps; + private int mDataStallTxPerThr; + private int mDataStallCcaLevelThr; + private int mLastFrequency = -1; + private String mLastBssid; + private long mLastTotalRadioOnFreqTimeMs = -1; + private long mLastTotalCcaBusyFreqTimeMs = -1; + private long mDataStallStartTimeMs = -1; + private Clock mClock; + private boolean mDataStallTx = false; + private boolean mDataStallRx = false; - public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics) { + public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics, + DeviceConfigFacade deviceConfigFacade, Looper clientModeImplLooper, Clock clock) { mContext = context; + mDeviceConfigFacade = deviceConfigFacade; mFacade = facade; + mHandler = new Handler(clientModeImplLooper); mWifiMetrics = wifiMetrics; + mClock = clock; loadSettings(); + + mDeviceConfigFacade.addOnPropertiesChangedListener( + command -> mHandler.post(command), + properties -> { + updateUsabilityDataCollectionFlags(); + }); } /** @@ -59,15 +93,18 @@ public class WifiDataStall { MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); mWifiMetrics.setWifiDataStallMinTxBad(mMinTxBad); mWifiMetrics.setWifiDataStallMinRxWithoutTx(mMinTxSuccessWithoutRx); + updateUsabilityDataCollectionFlags(); } /** * Checks for data stall by looking at tx/rx packet counts * @param oldStats second most recent WifiLinkLayerStats * @param newStats most recent WifiLinkLayerStats + * @param wifiInfo WifiInfo for current connection * @return trigger type of WifiIsUnusableEvent */ - public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats) { + public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats, + WifiInfo wifiInfo) { if (oldStats == null || newStats == null) { mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); return WifiIsUnusableEvent.TYPE_UNKNOWN; @@ -103,25 +140,130 @@ public class WifiDataStall { mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, txBadDelta, rxSuccessDelta, timeMsDelta); + if (timeMsDelta < MAX_MS_DELTA_FOR_DATA_STALL) { - // There is a data stall if there are too many tx failures - // or if we are not receiving any packets despite many tx successes - boolean dataStallBadTx = (txBadDelta >= mMinTxBad); - boolean dataStallTxSuccessWithoutRx = - (rxSuccessDelta == 0 && txSuccessDelta >= mMinTxSuccessWithoutRx); - if (dataStallBadTx && dataStallTxSuccessWithoutRx) { - mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH); - return WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; - } else if (dataStallBadTx) { - mWifiMetrics.logWifiIsUnusableEvent(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); - return WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; - } else if (dataStallTxSuccessWithoutRx) { - mWifiMetrics.logWifiIsUnusableEvent( - WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX); - return WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; + int txLinkSpeed = wifiInfo.getLinkSpeed(); + int rxLinkSpeed = wifiInfo.getRxLinkSpeedMbps(); + boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 + || (mLastBssid.equals(wifiInfo.getBSSID()) + && mLastFrequency == wifiInfo.getFrequency()); + mLastFrequency = wifiInfo.getFrequency(); + mLastBssid = wifiInfo.getBSSID(); + + int ccaLevel = updateCcaLevel(newStats, wifiInfo, isSameBssidAndFreq); + int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq); + + boolean isTxTputLow = false; + boolean isRxTputLow = false; + if (txLinkSpeed > 0) { + int txTput = txLinkSpeed * (100 - txPer) * (100 - ccaLevel); + isTxTputLow = txTput < mDataStallTxTputThrMbps * 100 * 100; + } + if (rxLinkSpeed > 0) { + int rxTput = rxLinkSpeed * (100 - ccaLevel); + isRxTputLow = rxTput < mDataStallRxTputThrMbps * 100; + } + + boolean dataStallTx = isTxTputLow || ccaLevel >= mDataStallCcaLevelThr + || txPer >= mDataStallTxPerThr; + boolean dataStallRx = isRxTputLow || ccaLevel >= mDataStallCcaLevelThr; + + // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls + // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall + if (dataStallTx || dataStallRx) { + mDataStallTx = mDataStallTx || dataStallTx; + mDataStallRx = mDataStallRx || dataStallRx; + if (mDataStallStartTimeMs == -1) { + mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); + if (mDataStallDurationMs == 0) { + mDataStallStartTimeMs = -1; + int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); + mDataStallRx = false; + mDataStallTx = false; + return result; + } + } else { + long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; + if (elapsedTime >= mDataStallDurationMs) { + mDataStallStartTimeMs = -1; + if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { + int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); + mDataStallRx = false; + mDataStallTx = false; + return result; + } else { + mDataStallTx = false; + mDataStallRx = false; + } + } else { + // No need to do anything. + } + } + } else { + mDataStallStartTimeMs = -1; + mDataStallTx = false; + mDataStallRx = false; } } return WifiIsUnusableEvent.TYPE_UNKNOWN; } + + private int updateCcaLevel(WifiLinkLayerStats newStats, WifiInfo wifiInfo, + boolean isSameBssidAndFreq) { + WifiLinkLayerStats.ChannelStats statsMap = newStats.channelStatsMap.get(mLastFrequency); + if (statsMap == null || !isSameBssidAndFreq) { + return DEFAULT_CCA_LEVEL; + } + int radioOnTimeDelta = (int) (statsMap.radioOnTimeMs - mLastTotalRadioOnFreqTimeMs); + int ccaBusyTimeDelta = (int) (statsMap.ccaBusyTimeMs - mLastTotalCcaBusyFreqTimeMs); + mLastTotalRadioOnFreqTimeMs = statsMap.radioOnTimeMs; + mLastTotalCcaBusyFreqTimeMs = statsMap.ccaBusyTimeMs; + + boolean isCcaValid = (radioOnTimeDelta > 0) && (ccaBusyTimeDelta >= 0) + && (ccaBusyTimeDelta <= radioOnTimeDelta); + // Update CCA level only if CCA stats are valid. + if (!isCcaValid) { + return DEFAULT_CCA_LEVEL; + } + return (int) (ccaBusyTimeDelta * 100 / radioOnTimeDelta); + } + + private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq) { + if (!isSameBssidAndFreq) { + return DEFAULT_TX_PACKET_ERROR_RATE; + } + long txAttempts = txSuccessDelta + txRetriesDelta; + if (txAttempts <= 0) { + return DEFAULT_TX_PACKET_ERROR_RATE; + } + return (int) (txRetriesDelta * 100 / txAttempts); + } + + private int calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx) { + int result = WifiIsUnusableEvent.TYPE_UNKNOWN; + if (dataStallTx && dataStallRx) { + result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; + } else if (dataStallTx) { + result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; + } else if (dataStallRx) { + result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; + } + mWifiMetrics.logWifiIsUnusableEvent(result); + return result; + } + + private void updateUsabilityDataCollectionFlags() { + mDataStallDurationMs = mDeviceConfigFacade.getDataStallDurationMs(); + mDataStallTxTputThrMbps = mDeviceConfigFacade.getDataStallTxTputThrMbps(); + mDataStallRxTputThrMbps = mDeviceConfigFacade.getDataStallRxTputThrMbps(); + mDataStallTxPerThr = mDeviceConfigFacade.getDataStallTxPerThr(); + mDataStallCcaLevelThr = mDeviceConfigFacade.getDataStallCcaLevelThr(); + + mWifiMetrics.setDataStallDurationMs(mDataStallDurationMs); + mWifiMetrics.setDataStallTxTputThrMbps(mDataStallTxTputThrMbps); + mWifiMetrics.setDataStallRxTputThrMbps(mDataStallRxTputThrMbps); + mWifiMetrics.setDataStallTxPerThr(mDataStallTxPerThr); + mWifiMetrics.setDataStallCcaLevelThr(mDataStallCcaLevelThr); + } } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 3a79e590b..fe9ebea17 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -301,7 +301,8 @@ public class WifiInjector { mWifiDiagnostics = new WifiDiagnostics( mContext, this, mWifiNative, mBuildProperties, new LastMileLogger(this), mClock); - mWifiDataStall = new WifiDataStall(mContext, mFrameworkFacade, mWifiMetrics); + mWifiDataStall = new WifiDataStall(mContext, mFrameworkFacade, mWifiMetrics, + mDeviceConfigFacade, clientModeImplLooper, mClock); mWifiMetrics.setWifiDataStall(mWifiDataStall); mLinkProbeManager = new LinkProbeManager(mClock, mWifiNative, mWifiMetrics, mFrameworkFacade, mWifiCoreHandlerThread.getLooper(), mContext); diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index c0b04d34c..1b7e8cdb3 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -2782,6 +2782,16 @@ public class WifiMetrics { + mExperimentValues.wifiDataStallMinTxSuccessWithoutRx); pw.println("mExperimentValues.linkSpeedCountsLoggingEnabled=" + mExperimentValues.linkSpeedCountsLoggingEnabled); + pw.println("mExperimentValues.dataStallDurationMs=" + + mExperimentValues.dataStallDurationMs); + pw.println("mExperimentValues.dataStallTxTputThrMbps=" + + mExperimentValues.dataStallTxTputThrMbps); + pw.println("mExperimentValues.dataStallRxTputThrMbps=" + + mExperimentValues.dataStallRxTputThrMbps); + pw.println("mExperimentValues.dataStallTxPerThr=" + + mExperimentValues.dataStallTxPerThr); + pw.println("mExperimentValues.dataStallCcaLevelThr=" + + mExperimentValues.dataStallCcaLevelThr); pw.println("WifiIsUnusableEventList: "); for (WifiIsUnusableWithTime event : mWifiIsUnusableList) { pw.println(event); @@ -5191,4 +5201,49 @@ public class WifiMetrics { mNumProvisionSuccess++; } } + + /** + * Sets the duration for evaluating Wifi condition to trigger a data stall + */ + public void setDataStallDurationMs(int duration) { + synchronized (mLock) { + mExperimentValues.dataStallDurationMs = duration; + } + } + + /** + * Sets the threshold of Tx throughput below which to trigger a data stall + */ + public void setDataStallTxTputThrMbps(int txTputThr) { + synchronized (mLock) { + mExperimentValues.dataStallTxTputThrMbps = txTputThr; + } + } + + /** + * Sets the threshold of Rx throughput below which to trigger a data stall + */ + public void setDataStallRxTputThrMbps(int rxTputThr) { + synchronized (mLock) { + mExperimentValues.dataStallRxTputThrMbps = rxTputThr; + } + } + + /** + * Sets the threshold of Tx packet error rate above which to trigger a data stall + */ + public void setDataStallTxPerThr(int txPerThr) { + synchronized (mLock) { + mExperimentValues.dataStallTxPerThr = txPerThr; + } + } + + /** + * Sets the threshold of CCA level above which to trigger a data stall + */ + public void setDataStallCcaLevelThr(int ccaLevel) { + synchronized (mLock) { + mExperimentValues.dataStallCcaLevelThr = ccaLevel; + } + } } diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index a26582c5c..7896a8f0b 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -3340,7 +3340,7 @@ public class ClientModeImplTest { when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(newLLStats); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); mLooper.dispatchAll(); - verify(mWifiDataStall).checkForDataStall(oldLLStats, newLLStats); + verify(mWifiDataStall).checkForDataStall(oldLLStats, newLLStats, mCmi.getWifiInfo()); verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(newLLStats); } @@ -3356,7 +3356,7 @@ public class ClientModeImplTest { WifiLinkLayerStats stats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); - when(mWifiDataStall.checkForDataStall(any(), any())) + when(mWifiDataStall.checkForDataStall(any(), any(), any())) .thenReturn(WifiIsUnusableEvent.TYPE_UNKNOWN); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); mLooper.dispatchAll(); @@ -3364,11 +3364,16 @@ public class ClientModeImplTest { verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, eq(anyInt()), eq(-1)); - when(mWifiDataStall.checkForDataStall(any(), any())) + when(mWifiDataStall.checkForDataStall(any(), any(), any())) .thenReturn(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); + when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); mLooper.dispatchAll(); verify(mWifiMetrics, times(2)).updateWifiUsabilityStatsEntries(any(), eq(stats)); + when(mClock.getElapsedSinceBootMillis()) + .thenReturn(10L + ClientModeImpl.DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS); + mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); + mLooper.dispatchAll(); verify(mWifiMetrics).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java index a5f6852f8..71b658923 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java @@ -17,14 +17,17 @@ 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.times; 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; @@ -33,6 +36,7 @@ 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; @@ -46,9 +50,15 @@ public class WifiDataStallTest { @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<OnPropertiesChangedListener> mOnPropertiesChangedListenerCaptor = + ArgumentCaptor.forClass(OnPropertiesChangedListener.class); /** * Sets up for unit test @@ -64,21 +74,38 @@ public class WifiDataStallTest { 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); + mWifiDataStall = new WifiDataStall(mContext, mFacade, mWifiMetrics, mDeviceConfigFacade, + mClientModeImplLooper, mClock); mOldLlStats.txmpdu_be = 1000; - mOldLlStats.retries_be = 2000; + mOldLlStats.retries_be = 1000; mOldLlStats.lostmpdu_be = 3000; mOldLlStats.rxmpdu_be = 4000; mOldLlStats.timeStampInMs = 10000; - mNewLlStats.txmpdu_be = mOldLlStats.txmpdu_be; - mNewLlStats.retries_be = mOldLlStats.retries_be; + 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()); } /** @@ -98,23 +125,53 @@ public class WifiDataStallTest { */ @Test public void verifyDataStallTxFailure() throws Exception { - mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT; - assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + 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 { - mNewLlStats.txmpdu_be = - mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT; - assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + 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); } @@ -124,58 +181,61 @@ public class WifiDataStallTest { */ @Test public void verifyDataStallBothTxRxFailure() throws Exception { - mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT; - mNewLlStats.txmpdu_be = - mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT; - assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + 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 minimum number of tx failures - * to trigger data stall from settings + * Verify that we can change the duration of evaluating Wifi conditions + * to trigger data stall from DeviceConfigFacade */ @Test - public void verifyDataStallTxFailureSettingsChange() throws Exception { - when(mFacade.getIntegerSetting(mContext, - Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD, - WifiDataStall.MIN_TX_BAD_DEFAULT)) - .thenReturn(WifiDataStall.MIN_TX_BAD_DEFAULT + 1); - mWifiDataStall.loadSettings(); - verify(mWifiMetrics).setWifiDataStallMinTxBad(WifiDataStall.MIN_TX_BAD_DEFAULT + 1); - verify(mWifiMetrics, times(2)).setWifiDataStallMinRxWithoutTx( - WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); + 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); - mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); - verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); + 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 minimum number of tx successes when rx success is 0 - * to trigger data stall from settings + * Verify that we can change the threshold of Tx packet error rate to trigger a data stall + * from DeviceConfigFacade */ @Test - public void verifyDataStallRxFailureSettingsChange() throws Exception { - 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 + 1); - mWifiDataStall.loadSettings(); - verify(mWifiMetrics, times(2)).setWifiDataStallMinTxBad(WifiDataStall.MIN_TX_BAD_DEFAULT); - verify(mWifiMetrics).setWifiDataStallMinRxWithoutTx( - WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT + 1); - - mNewLlStats.txmpdu_be = - mOldLlStats.txmpdu_be + WifiDataStall.MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT; + 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)); + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); - verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); + 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); } /** @@ -184,7 +244,7 @@ public class WifiDataStallTest { @Test public void verifyNoDataStallWhenNoFail() throws Exception { assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics, never()).resetWifiIsUnusableLinkLayerStats(); verifyUpdateWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); @@ -196,11 +256,10 @@ public class WifiDataStallTest { */ @Test public void verifyNoDataStallBigTimeGap() throws Exception { - mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be + WifiDataStall.MIN_TX_BAD_DEFAULT; mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + WifiDataStall.MAX_MS_DELTA_FOR_DATA_STALL + 1; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verifyUpdateWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(anyInt()); } @@ -212,7 +271,7 @@ public class WifiDataStallTest { public void verifyReset() throws Exception { mNewLlStats.lostmpdu_be = mOldLlStats.lostmpdu_be - 1; assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, - mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats)); + mWifiDataStall.checkForDataStall(mOldLlStats, mNewLlStats, mWifiInfo)); verify(mWifiMetrics).resetWifiIsUnusableLinkLayerStats(); verify(mWifiMetrics, never()).updateWifiIsUnusableLinkLayerStats( anyLong(), anyLong(), anyLong(), anyLong(), anyLong()); |