summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-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
5 files changed, 290 insertions, 23 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;
+ }
+ }
}