1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.content.Context; 20 import android.net.wifi.WifiInfo; 21 import android.os.Handler; 22 import android.os.Looper; 23 import android.provider.Settings; 24 25 import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; 26 27 /** 28 * Looks for Wifi data stalls 29 */ 30 public class WifiDataStall { 31 32 // Default minimum number of txBadDelta to trigger data stall 33 public static final int MIN_TX_BAD_DEFAULT = 1; 34 // Default minimum number of txSuccessDelta to trigger data stall 35 // when rxSuccessDelta is 0 36 public static final int MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT = 50; 37 // Maximum time gap between two WifiLinkLayerStats to trigger a data stall 38 public static final long MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute 39 // Maximum time that a data stall start time stays valid. 40 public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes 41 // Default Tx packet error rate when there is no Tx attempt 42 public static final int DEFAULT_TX_PACKET_ERROR_RATE = 20; 43 // Default CCA level when CCA stats are not available 44 public static final int DEFAULT_CCA_LEVEL = 0; 45 46 private final Context mContext; 47 private final DeviceConfigFacade mDeviceConfigFacade; 48 private final FrameworkFacade mFacade; 49 private final WifiMetrics mWifiMetrics; 50 51 private Handler mHandler; 52 private int mMinTxBad; 53 private int mMinTxSuccessWithoutRx; 54 private int mDataStallDurationMs; 55 private int mDataStallTxTputThrMbps; 56 private int mDataStallRxTputThrMbps; 57 private int mDataStallTxPerThr; 58 private int mDataStallCcaLevelThr; 59 private int mLastFrequency = -1; 60 private String mLastBssid; 61 private long mLastTotalRadioOnFreqTimeMs = -1; 62 private long mLastTotalCcaBusyFreqTimeMs = -1; 63 private long mDataStallStartTimeMs = -1; 64 private Clock mClock; 65 private boolean mDataStallTx = false; 66 private boolean mDataStallRx = false; 67 WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics, DeviceConfigFacade deviceConfigFacade, Looper clientModeImplLooper, Clock clock)68 public WifiDataStall(Context context, FrameworkFacade facade, WifiMetrics wifiMetrics, 69 DeviceConfigFacade deviceConfigFacade, Looper clientModeImplLooper, Clock clock) { 70 mContext = context; 71 mDeviceConfigFacade = deviceConfigFacade; 72 mFacade = facade; 73 mHandler = new Handler(clientModeImplLooper); 74 mWifiMetrics = wifiMetrics; 75 mClock = clock; 76 loadSettings(); 77 78 mDeviceConfigFacade.addOnPropertiesChangedListener( 79 command -> mHandler.post(command), 80 properties -> { 81 updateUsabilityDataCollectionFlags(); 82 }); 83 } 84 85 /** 86 * Load setting values related to wifi data stall. 87 */ loadSettings()88 public void loadSettings() { 89 mMinTxBad = mFacade.getIntegerSetting( 90 mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD, MIN_TX_BAD_DEFAULT); 91 mMinTxSuccessWithoutRx = mFacade.getIntegerSetting( 92 mContext, Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX, 93 MIN_TX_SUCCESS_WITHOUT_RX_DEFAULT); 94 mWifiMetrics.setWifiDataStallMinTxBad(mMinTxBad); 95 mWifiMetrics.setWifiDataStallMinRxWithoutTx(mMinTxSuccessWithoutRx); 96 updateUsabilityDataCollectionFlags(); 97 } 98 99 /** 100 * Checks for data stall by looking at tx/rx packet counts 101 * @param oldStats second most recent WifiLinkLayerStats 102 * @param newStats most recent WifiLinkLayerStats 103 * @param wifiInfo WifiInfo for current connection 104 * @return trigger type of WifiIsUnusableEvent 105 */ checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats, WifiInfo wifiInfo)106 public int checkForDataStall(WifiLinkLayerStats oldStats, WifiLinkLayerStats newStats, 107 WifiInfo wifiInfo) { 108 if (oldStats == null || newStats == null) { 109 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 110 return WifiIsUnusableEvent.TYPE_UNKNOWN; 111 } 112 113 long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk 114 + newStats.txmpdu_vi + newStats.txmpdu_vo) 115 - (oldStats.txmpdu_be + oldStats.txmpdu_bk 116 + oldStats.txmpdu_vi + oldStats.txmpdu_vo); 117 long txRetriesDelta = (newStats.retries_be + newStats.retries_bk 118 + newStats.retries_vi + newStats.retries_vo) 119 - (oldStats.retries_be + oldStats.retries_bk 120 + oldStats.retries_vi + oldStats.retries_vo); 121 long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk 122 + newStats.lostmpdu_vi + newStats.lostmpdu_vo) 123 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk 124 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo); 125 long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk 126 + newStats.rxmpdu_vi + newStats.rxmpdu_vo) 127 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk 128 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo); 129 long timeMsDelta = newStats.timeStampInMs - oldStats.timeStampInMs; 130 131 if (timeMsDelta < 0 132 || txSuccessDelta < 0 133 || txRetriesDelta < 0 134 || txBadDelta < 0 135 || rxSuccessDelta < 0) { 136 // There was a reset in WifiLinkLayerStats 137 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 138 return WifiIsUnusableEvent.TYPE_UNKNOWN; 139 } 140 141 mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, 142 txBadDelta, rxSuccessDelta, timeMsDelta); 143 144 if (timeMsDelta < MAX_MS_DELTA_FOR_DATA_STALL) { 145 int txLinkSpeed = wifiInfo.getLinkSpeed(); 146 int rxLinkSpeed = wifiInfo.getRxLinkSpeedMbps(); 147 boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 148 || (mLastBssid.equals(wifiInfo.getBSSID()) 149 && mLastFrequency == wifiInfo.getFrequency()); 150 mLastFrequency = wifiInfo.getFrequency(); 151 mLastBssid = wifiInfo.getBSSID(); 152 153 int ccaLevel = updateCcaLevel(newStats, wifiInfo, isSameBssidAndFreq); 154 int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq); 155 156 boolean isTxTputLow = false; 157 boolean isRxTputLow = false; 158 if (txLinkSpeed > 0) { 159 int txTput = txLinkSpeed * (100 - txPer) * (100 - ccaLevel); 160 isTxTputLow = txTput < mDataStallTxTputThrMbps * 100 * 100; 161 } 162 if (rxLinkSpeed > 0) { 163 int rxTput = rxLinkSpeed * (100 - ccaLevel); 164 isRxTputLow = rxTput < mDataStallRxTputThrMbps * 100; 165 } 166 167 boolean dataStallTx = isTxTputLow || ccaLevel >= mDataStallCcaLevelThr 168 || txPer >= mDataStallTxPerThr; 169 boolean dataStallRx = isRxTputLow || ccaLevel >= mDataStallCcaLevelThr; 170 171 // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls 172 // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall 173 if (dataStallTx || dataStallRx) { 174 mDataStallTx = mDataStallTx || dataStallTx; 175 mDataStallRx = mDataStallRx || dataStallRx; 176 if (mDataStallStartTimeMs == -1) { 177 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); 178 if (mDataStallDurationMs == 0) { 179 mDataStallStartTimeMs = -1; 180 int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); 181 mDataStallRx = false; 182 mDataStallTx = false; 183 return result; 184 } 185 } else { 186 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; 187 if (elapsedTime >= mDataStallDurationMs) { 188 mDataStallStartTimeMs = -1; 189 if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { 190 int result = calculateUsabilityEventType(mDataStallTx, mDataStallRx); 191 mDataStallRx = false; 192 mDataStallTx = false; 193 return result; 194 } else { 195 mDataStallTx = false; 196 mDataStallRx = false; 197 } 198 } else { 199 // No need to do anything. 200 } 201 } 202 } else { 203 mDataStallStartTimeMs = -1; 204 mDataStallTx = false; 205 mDataStallRx = false; 206 } 207 } 208 209 return WifiIsUnusableEvent.TYPE_UNKNOWN; 210 } 211 updateCcaLevel(WifiLinkLayerStats newStats, WifiInfo wifiInfo, boolean isSameBssidAndFreq)212 private int updateCcaLevel(WifiLinkLayerStats newStats, WifiInfo wifiInfo, 213 boolean isSameBssidAndFreq) { 214 WifiLinkLayerStats.ChannelStats statsMap = newStats.channelStatsMap.get(mLastFrequency); 215 if (statsMap == null || !isSameBssidAndFreq) { 216 return DEFAULT_CCA_LEVEL; 217 } 218 int radioOnTimeDelta = (int) (statsMap.radioOnTimeMs - mLastTotalRadioOnFreqTimeMs); 219 int ccaBusyTimeDelta = (int) (statsMap.ccaBusyTimeMs - mLastTotalCcaBusyFreqTimeMs); 220 mLastTotalRadioOnFreqTimeMs = statsMap.radioOnTimeMs; 221 mLastTotalCcaBusyFreqTimeMs = statsMap.ccaBusyTimeMs; 222 223 boolean isCcaValid = (radioOnTimeDelta > 0) && (ccaBusyTimeDelta >= 0) 224 && (ccaBusyTimeDelta <= radioOnTimeDelta); 225 // Update CCA level only if CCA stats are valid. 226 if (!isCcaValid) { 227 return DEFAULT_CCA_LEVEL; 228 } 229 return (int) (ccaBusyTimeDelta * 100 / radioOnTimeDelta); 230 } 231 updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq)232 private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq) { 233 if (!isSameBssidAndFreq) { 234 return DEFAULT_TX_PACKET_ERROR_RATE; 235 } 236 long txAttempts = txSuccessDelta + txRetriesDelta; 237 if (txAttempts <= 0) { 238 return DEFAULT_TX_PACKET_ERROR_RATE; 239 } 240 return (int) (txRetriesDelta * 100 / txAttempts); 241 } 242 calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx)243 private int calculateUsabilityEventType(boolean dataStallTx, boolean dataStallRx) { 244 int result = WifiIsUnusableEvent.TYPE_UNKNOWN; 245 if (dataStallTx && dataStallRx) { 246 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; 247 } else if (dataStallTx) { 248 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; 249 } else if (dataStallRx) { 250 result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; 251 } 252 mWifiMetrics.logWifiIsUnusableEvent(result); 253 return result; 254 } 255 updateUsabilityDataCollectionFlags()256 private void updateUsabilityDataCollectionFlags() { 257 mDataStallDurationMs = mDeviceConfigFacade.getDataStallDurationMs(); 258 mDataStallTxTputThrMbps = mDeviceConfigFacade.getDataStallTxTputThrMbps(); 259 mDataStallRxTputThrMbps = mDeviceConfigFacade.getDataStallRxTputThrMbps(); 260 mDataStallTxPerThr = mDeviceConfigFacade.getDataStallTxPerThr(); 261 mDataStallCcaLevelThr = mDeviceConfigFacade.getDataStallCcaLevelThr(); 262 263 mWifiMetrics.setDataStallDurationMs(mDataStallDurationMs); 264 mWifiMetrics.setDataStallTxTputThrMbps(mDataStallTxTputThrMbps); 265 mWifiMetrics.setDataStallRxTputThrMbps(mDataStallRxTputThrMbps); 266 mWifiMetrics.setDataStallTxPerThr(mDataStallTxPerThr); 267 mWifiMetrics.setDataStallCcaLevelThr(mDataStallCcaLevelThr); 268 } 269 } 270