diff options
Diffstat (limited to 'service')
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; + } + } } |