summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMingguang Xu <mingguangxu@google.com>2019-09-13 17:27:13 -0700
committerMingguang Xu <mingguangxu@google.com>2019-09-18 02:09:09 -0700
commit9a3473bafbffd386b54743500ac875f10bab459b (patch)
treeaabf472e2f90a493ecb836020081b09cd3843dcf
parent8c75b6b98895a732d715e52d034b905fbc132f2c (diff)
Wifi usability: Data stall detection based on link layer stats
This is an effort to define new criterion for triggering Wifi data stall: (1) Defined data stall criterion based on transmit and/or receive throughput, transmit packet error rate, and CCA level. Data stall is triggered when transmit and/or receive links are consecutively bad over multiple RSSI polls. (2) Added DeviceConfig flags (that is disabled by default) which may be configured on the server side to tune the thresholds at which data stall gets triggered. Bug: 141027476 Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh Change-Id: I3c5d9756ff74c0b5682a0b2a51ed64c27c679d72 Signed-off-by: Mingguang Xu <mingguangxu@google.com> Merged-In: I76d6338cd2d482d198fde1e5a2d1a0540c087ca6
-rw-r--r--service/java/com/android/server/wifi/ClientModeImpl.java28
-rw-r--r--service/java/com/android/server/wifi/DeviceConfigFacade.java51
-rw-r--r--service/java/com/android/server/wifi/WifiDataStall.java176
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java3
-rw-r--r--service/java/com/android/server/wifi/WifiMetrics.java55
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java11
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java161
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());