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 static com.android.server.wifi.util.InformationElementUtil.BssLoad.CHANNEL_UTILIZATION_SCALE; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.net.wifi.WifiInfo; 26 import android.net.wifi.WifiManager.DeviceMobilityState; 27 import android.os.Handler; 28 import android.telephony.PhoneStateListener; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.TelephonyManager; 31 import android.util.Log; 32 33 import com.android.modules.utils.HandlerExecutor; 34 import com.android.server.wifi.ActiveModeWarden.PrimaryClientModeManagerChangedCallback; 35 import com.android.server.wifi.WifiNative.ConnectionCapabilities; 36 import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent; 37 import com.android.server.wifi.util.InformationElementUtil.BssLoad; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 42 /** 43 * Looks for Wifi data stalls 44 */ 45 public class WifiDataStall { 46 private static final String TAG = "WifiDataStall"; 47 private boolean mVerboseLoggingEnabled = false; 48 public static final int INVALID_THROUGHPUT = -1; 49 // Maximum time gap between two WifiLinkLayerStats to trigger a data stall 50 public static final int MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute 51 // Maximum time that a data stall start time stays valid. 52 public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes 53 // Default Tx packet error rate when there is no Tx attempt 54 public static final int DEFAULT_TX_PACKET_ERROR_RATE = 5; 55 // Default CCA level when CCA stats are not available 56 public static final int DEFAULT_CCA_LEVEL_2G = CHANNEL_UTILIZATION_SCALE * 16 / 100; 57 public static final int DEFAULT_CCA_LEVEL_ABOVE_2G = CHANNEL_UTILIZATION_SCALE * 6 / 100; 58 // Minimum time interval in ms between two link layer stats cache updates 59 private static final int LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS = 30_000; 60 // Maximum time margin between two link layer stats for connection duration update 61 public static final int MAX_TIME_MARGIN_LAST_TWO_POLLS_MS = 200; 62 63 private final DeviceConfigFacade mDeviceConfigFacade; 64 private final WifiMetrics mWifiMetrics; 65 private final Context mContext; 66 private final WifiChannelUtilization mWifiChannelUtilization; 67 private TelephonyManager mTelephonyManager; 68 private final ThroughputPredictor mThroughputPredictor; 69 private final ActiveModeWarden mActiveModeWarden; 70 private final ClientModeImplMonitor mClientModeImplMonitor; 71 private final WifiGlobals mWifiGlobals; 72 73 private int mLastFrequency = -1; 74 private String mLastBssid; 75 private long mDataStallStartTimeMs = -1; 76 private Clock mClock; 77 private boolean mDataStallTx = false; 78 private boolean mDataStallRx = false; 79 private long mLastTxBytes; 80 private long mLastRxBytes; 81 private boolean mIsThroughputSufficient = true; 82 private boolean mIsCellularDataAvailable = false; 83 private final PhoneStateListener mPhoneStateListener; 84 private boolean mPhoneStateListenerEnabled = false; 85 private int mTxTputKbps = INVALID_THROUGHPUT; 86 private int mRxTputKbps = INVALID_THROUGHPUT; 87 88 /** @hide */ 89 @IntDef(prefix = { "CELLULAR_DATA_" }, value = { 90 CELLULAR_DATA_UNKNOWN, 91 CELLULAR_DATA_AVAILABLE, 92 CELLULAR_DATA_NOT_AVAILABLE, 93 }) 94 @Retention(RetentionPolicy.SOURCE) 95 public @interface CellularDataStatusCode {} 96 public static final int CELLULAR_DATA_UNKNOWN = 0; 97 public static final int CELLULAR_DATA_AVAILABLE = 1; 98 public static final int CELLULAR_DATA_NOT_AVAILABLE = 2; 99 100 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 101 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)102 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 103 update(); 104 } 105 106 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)107 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 108 update(); 109 } 110 111 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)112 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 113 update(); 114 } 115 update()116 private void update() { 117 // Register/Unregister phone listener on wifi on/off. 118 if (mActiveModeWarden.getPrimaryClientModeManagerNullable() != null) { 119 enablePhoneStateListener(); 120 } else { 121 disablePhoneStateListener(); 122 } 123 } 124 } 125 126 private class PrimaryModeChangeCallback implements PrimaryClientModeManagerChangedCallback { 127 @Override onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)128 public void onChange( 129 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager, 130 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) { 131 // This is needed to reset state on an MBB switch or wifi toggle. 132 if (prevPrimaryClientModeManager != null) { 133 reset(); 134 } 135 if (newPrimaryClientModeManager != null) { 136 init(); 137 } 138 } 139 } 140 141 private class ClientModeImplListenerInternal implements ClientModeImplListener { 142 @Override onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)143 public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) { 144 if (clientModeManager.getRole() == ActiveModeManager.ROLE_CLIENT_PRIMARY) { 145 reset(); 146 } 147 } 148 } 149 WifiDataStall(WifiMetrics wifiMetrics, Context context, DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, Clock clock, Handler handler, ThroughputPredictor throughputPredictor, ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor, WifiGlobals wifiGlobals)150 public WifiDataStall(WifiMetrics wifiMetrics, Context context, 151 DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, 152 Clock clock, Handler handler, ThroughputPredictor throughputPredictor, 153 ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor, 154 WifiGlobals wifiGlobals) { 155 mDeviceConfigFacade = deviceConfigFacade; 156 mWifiMetrics = wifiMetrics; 157 mContext = context; 158 mClock = clock; 159 mWifiChannelUtilization = wifiChannelUtilization; 160 mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS); 161 mThroughputPredictor = throughputPredictor; 162 mActiveModeWarden = activeModeWarden; 163 mClientModeImplMonitor = clientModeImplMonitor; 164 mWifiGlobals = wifiGlobals; 165 mPhoneStateListener = new PhoneStateListener(new HandlerExecutor(handler)) { 166 @Override 167 public void onDataConnectionStateChanged(int state, int networkType) { 168 if (state != TelephonyManager.DATA_CONNECTED 169 && state != TelephonyManager.DATA_DISCONNECTED) { 170 Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state); 171 return; 172 } 173 mIsCellularDataAvailable = state == TelephonyManager.DATA_CONNECTED; 174 mActiveModeWarden.getPrimaryClientModeManager() 175 .onCellularConnectivityChanged(mIsCellularDataAvailable 176 ? CELLULAR_DATA_AVAILABLE : CELLULAR_DATA_NOT_AVAILABLE); 177 logd("Cellular Data: " + mIsCellularDataAvailable); 178 } 179 }; 180 mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback( 181 new PrimaryModeChangeCallback()); 182 mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 183 mClientModeImplMonitor.registerListener(new ClientModeImplListenerInternal()); 184 } 185 186 /** 187 * initialization after wifi is enabled 188 */ init()189 private void init() { 190 mWifiChannelUtilization.init(null); 191 reset(); 192 } 193 194 /** 195 * Reset internal variables 196 */ reset()197 private void reset() { 198 mLastTxBytes = 0; 199 mLastRxBytes = 0; 200 mLastFrequency = -1; 201 mLastBssid = null; 202 mDataStallStartTimeMs = -1; 203 mDataStallTx = false; 204 mDataStallRx = false; 205 mIsThroughputSufficient = true; 206 mTxTputKbps = INVALID_THROUGHPUT; 207 mRxTputKbps = INVALID_THROUGHPUT; 208 } 209 createTelephonyManagerForDefaultDataSubIfNeeded()210 private void createTelephonyManagerForDefaultDataSubIfNeeded() { 211 if (mTelephonyManager == null) { 212 mTelephonyManager = (TelephonyManager) mContext 213 .getSystemService(Context.TELEPHONY_SERVICE); 214 } 215 int defaultSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); 216 if (defaultSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 217 && defaultSubscriptionId != mTelephonyManager.getSubscriptionId()) { 218 mTelephonyManager = mTelephonyManager.createForSubscriptionId( 219 SubscriptionManager.getDefaultDataSubscriptionId()); 220 } 221 } 222 223 /** 224 * Reset the PhoneStateListener to listen on the default data SIM. 225 */ resetPhoneStateListener()226 public void resetPhoneStateListener() { 227 disablePhoneStateListener(); 228 mActiveModeWarden.getPrimaryClientModeManager() 229 .onCellularConnectivityChanged(CELLULAR_DATA_UNKNOWN); 230 enablePhoneStateListener(); 231 } 232 233 /** 234 * Enable phone state listener 235 */ enablePhoneStateListener()236 private void enablePhoneStateListener() { 237 createTelephonyManagerForDefaultDataSubIfNeeded(); 238 if (mTelephonyManager != null && !mPhoneStateListenerEnabled) { 239 mPhoneStateListenerEnabled = true; 240 mTelephonyManager.listen(mPhoneStateListener, 241 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); 242 } 243 } 244 245 /** 246 * Disable phone state listener 247 */ disablePhoneStateListener()248 private void disablePhoneStateListener() { 249 if (mTelephonyManager != null && mPhoneStateListenerEnabled) { 250 mPhoneStateListenerEnabled = false; 251 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 252 } 253 } 254 255 /** 256 * Enable/Disable verbose logging. 257 * @param verbose true to enable and false to disable. 258 */ enableVerboseLogging(boolean verbose)259 public void enableVerboseLogging(boolean verbose) { 260 mVerboseLoggingEnabled = verbose; 261 mWifiChannelUtilization.enableVerboseLogging(verbose); 262 } 263 264 /** 265 * Update device mobility state 266 * @param newState the new device mobility state 267 */ setDeviceMobilityState(@eviceMobilityState int newState)268 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 269 mWifiChannelUtilization.setDeviceMobilityState(newState); 270 } 271 272 /** 273 * Check if current link layer throughput is sufficient. 274 * This should be called after checkDataStallAndThroughputSufficiency(). 275 * @return true if it is sufficient or false if it is insufficient 276 */ isThroughputSufficient()277 public boolean isThroughputSufficient() { 278 return mIsThroughputSufficient; 279 } 280 281 /** 282 * Check if cellular data is available 283 * @return true if it is available and false otherwise 284 */ isCellularDataAvailable()285 public boolean isCellularDataAvailable() { 286 return mIsCellularDataAvailable; 287 } 288 289 /** 290 * Get the latest Tx throughput based on Tx link speed, PER and channel utilization 291 * @return the latest estimated Tx throughput in Kbps if it is available 292 * or INVALID_THROUGHPUT if it is not available 293 */ getTxThroughputKbps()294 public int getTxThroughputKbps() { 295 logd("tx tput in kbps: " + mTxTputKbps); 296 return mTxTputKbps; 297 } 298 299 /** 300 * Get the latest Rx throughput based on Rx link speed and channel utilization 301 * @return the latest estimated Rx throughput in Kbps if it is available 302 * or INVALID_THROUGHPUT if it is not available 303 */ getRxThroughputKbps()304 public int getRxThroughputKbps() { 305 logd("rx tput in kbps: " + mRxTputKbps); 306 return mRxTputKbps; 307 } 308 309 /** 310 * Update data stall detection, check throughput sufficiency and report wifi health stat 311 * with the latest link layer stats 312 * @param connectionCapabilities Connection capabilities. 313 * @param oldStats second most recent WifiLinkLayerStats 314 * @param newStats most recent WifiLinkLayerStats 315 * @param wifiInfo WifiInfo for current connection 316 * @return trigger type of WifiIsUnusableEvent 317 * 318 * Note: This is only collected for primary STA currently because RSSI polling is disabled for 319 * non-primary STAs. 320 */ checkDataStallAndThroughputSufficiency( @onNull String ifaceName, @NonNull ConnectionCapabilities connectionCapabilities, @Nullable WifiLinkLayerStats oldStats, @Nullable WifiLinkLayerStats newStats, @NonNull WifiInfo wifiInfo, long txBytes, long rxBytes)321 public int checkDataStallAndThroughputSufficiency( 322 @NonNull String ifaceName, 323 @NonNull ConnectionCapabilities connectionCapabilities, 324 @Nullable WifiLinkLayerStats oldStats, 325 @Nullable WifiLinkLayerStats newStats, 326 @NonNull WifiInfo wifiInfo, 327 long txBytes, long rxBytes) { 328 int currFrequency = wifiInfo.getFrequency(); 329 mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency); 330 int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency); 331 mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency); 332 if (oldStats == null || newStats == null) { 333 // First poll after new association 334 // Update throughput with prediction 335 if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && connectionCapabilities != null) { 336 mTxTputKbps = mThroughputPredictor.predictTxThroughput(connectionCapabilities, 337 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 338 mRxTputKbps = mThroughputPredictor.predictRxThroughput(connectionCapabilities, 339 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 340 } 341 mIsThroughputSufficient = true; 342 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 343 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 344 return WifiIsUnusableEvent.TYPE_UNKNOWN; 345 } 346 347 long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk 348 + newStats.txmpdu_vi + newStats.txmpdu_vo) 349 - (oldStats.txmpdu_be + oldStats.txmpdu_bk 350 + oldStats.txmpdu_vi + oldStats.txmpdu_vo); 351 long txRetriesDelta = (newStats.retries_be + newStats.retries_bk 352 + newStats.retries_vi + newStats.retries_vo) 353 - (oldStats.retries_be + oldStats.retries_bk 354 + oldStats.retries_vi + oldStats.retries_vo); 355 long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk 356 + newStats.lostmpdu_vi + newStats.lostmpdu_vo) 357 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk 358 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo); 359 long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk 360 + newStats.rxmpdu_vi + newStats.rxmpdu_vo) 361 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk 362 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo); 363 int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs); 364 365 long totalTxDelta = txSuccessDelta + txRetriesDelta; 366 boolean isTxTrafficHigh = (totalTxDelta * 1000) 367 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 368 boolean isRxTrafficHigh = (rxSuccessDelta * 1000) 369 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 370 if (timeDeltaLastTwoPollsMs < 0 371 || txSuccessDelta < 0 372 || txRetriesDelta < 0 373 || txBadDelta < 0 374 || rxSuccessDelta < 0) { 375 mIsThroughputSufficient = true; 376 // There was a reset in WifiLinkLayerStats 377 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 378 return WifiIsUnusableEvent.TYPE_UNKNOWN; 379 } 380 381 mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, 382 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs); 383 384 int txLinkSpeedMbps = wifiInfo.getLinkSpeed(); 385 int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps(); 386 boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 387 || (mLastBssid.equals(wifiInfo.getBSSID()) 388 && mLastFrequency == currFrequency); 389 mLastFrequency = currFrequency; 390 mLastBssid = wifiInfo.getBSSID(); 391 392 if (ccaLevel == BssLoad.INVALID) { 393 ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G; 394 logd(" use default cca Level"); 395 } 396 logd(" ccaLevel = " + ccaLevel); 397 398 int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq, 399 isTxTrafficHigh); 400 401 boolean isTxTputLow = false; 402 boolean isRxTputLow = false; 403 404 if (txLinkSpeedMbps > 0) { 405 // Exclude update with low rate management frames 406 if (isTxTrafficHigh 407 || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) { 408 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100 409 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 410 } 411 isTxTputLow = mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps(); 412 } else { 413 mTxTputKbps = INVALID_THROUGHPUT; 414 } 415 416 if (rxLinkSpeedMbps > 0) { 417 // Exclude update with low rate management frames 418 if (isRxTrafficHigh 419 || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) { 420 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000 421 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 422 } 423 isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps(); 424 } else { 425 mRxTputKbps = INVALID_THROUGHPUT; 426 } 427 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 428 429 mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps, 430 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs, txBytes, rxBytes); 431 432 int maxTimeDeltaMs = mWifiGlobals.getPollRssiIntervalMillis() 433 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS; 434 if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) { 435 mWifiMetrics.incrementConnectionDuration(ifaceName, timeDeltaLastTwoPollsMs, 436 mIsThroughputSufficient, mIsCellularDataAvailable, wifiInfo.getRssi(), 437 mTxTputKbps, mRxTputKbps); 438 } 439 440 boolean possibleDataStallTx = isTxTputLow 441 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr() 442 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr(); 443 boolean possibleDataStallRx = isRxTputLow 444 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr(); 445 446 boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx; 447 boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx; 448 449 return detectConsecutiveTwoDataStalls(ifaceName, timeDeltaLastTwoPollsMs, dataStallTx, 450 dataStallRx); 451 } 452 453 // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls 454 // 1st data stall should be preceded by no data stall 455 // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)456 private int detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, 457 boolean dataStallTx, boolean dataStallRx) { 458 if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) { 459 return WifiIsUnusableEvent.TYPE_UNKNOWN; 460 } 461 462 if (dataStallTx || dataStallRx) { 463 mDataStallTx = mDataStallTx || dataStallTx; 464 mDataStallRx = mDataStallRx || dataStallRx; 465 if (mDataStallStartTimeMs == -1) { 466 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); 467 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) { 468 mDataStallStartTimeMs = -1; 469 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 470 mDataStallRx); 471 mDataStallRx = false; 472 mDataStallTx = false; 473 return result; 474 } 475 } else { 476 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; 477 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) { 478 mDataStallStartTimeMs = -1; 479 if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { 480 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 481 mDataStallRx); 482 mDataStallRx = false; 483 mDataStallTx = false; 484 return result; 485 } else { 486 mDataStallTx = false; 487 mDataStallRx = false; 488 } 489 } else { 490 // No need to do anything. 491 } 492 } 493 } else { 494 mDataStallStartTimeMs = -1; 495 mDataStallTx = false; 496 mDataStallRx = false; 497 } 498 return WifiIsUnusableEvent.TYPE_UNKNOWN; 499 } 500 updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)501 private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, 502 boolean isTxTrafficHigh) { 503 if (!isSameBssidAndFreq) { 504 return DEFAULT_TX_PACKET_ERROR_RATE; 505 } 506 long txAttempts = txSuccessDelta + txRetriesDelta; 507 if (txAttempts <= 0 || !isTxTrafficHigh) { 508 return DEFAULT_TX_PACKET_ERROR_RATE; 509 } 510 return (int) (txRetriesDelta * 100 / txAttempts); 511 } calculateUsabilityEventType(String ifaceName, boolean dataStallTx, boolean dataStallRx)512 private int calculateUsabilityEventType(String ifaceName, boolean dataStallTx, 513 boolean dataStallRx) { 514 int result = WifiIsUnusableEvent.TYPE_UNKNOWN; 515 if (dataStallTx && dataStallRx) { 516 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; 517 } else if (dataStallTx) { 518 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; 519 } else if (dataStallRx) { 520 result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; 521 } 522 mWifiMetrics.logWifiIsUnusableEvent(ifaceName, result); 523 return result; 524 } 525 isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs, long txBytes, long rxBytes)526 private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, 527 boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs, 528 long txBytes, long rxBytes) { 529 if (timeDeltaLastTwoPollsMs > MAX_MS_DELTA_FOR_DATA_STALL 530 || mLastTxBytes == 0 || mLastRxBytes == 0) { 531 mLastTxBytes = txBytes; 532 mLastRxBytes = rxBytes; 533 return true; 534 } 535 536 int l3TxTputKbps = (int) ((txBytes - mLastTxBytes) * 8 / timeDeltaLastTwoPollsMs); 537 int l3RxTputKbps = (int) ((rxBytes - mLastRxBytes) * 8 / timeDeltaLastTwoPollsMs); 538 539 mLastTxBytes = txBytes; 540 mLastRxBytes = rxBytes; 541 542 boolean isTxTputSufficient = isL2ThroughputSufficient(l2TxTputKbps, l3TxTputKbps, false); 543 boolean isRxTputSufficient = isL2ThroughputSufficient(l2RxTputKbps, l3RxTputKbps, true); 544 isTxTputSufficient = detectAndOverrideFalseInSufficient( 545 isTxTputSufficient, isTxTrafficHigh, mIsThroughputSufficient); 546 isRxTputSufficient = detectAndOverrideFalseInSufficient( 547 isRxTputSufficient, isRxTrafficHigh, mIsThroughputSufficient); 548 549 boolean isThroughputSufficient = isTxTputSufficient && isRxTputSufficient; 550 551 StringBuilder sb = new StringBuilder(); 552 logd(sb.append("L2 txTputKbps: ").append(l2TxTputKbps) 553 .append(", rxTputKbps: ").append(l2RxTputKbps) 554 .append(", L3 txTputKbps: ").append(l3TxTputKbps) 555 .append(", rxTputKbps: ").append(l3RxTputKbps) 556 .append(", TxTrafficHigh: ").append(isTxTrafficHigh) 557 .append(", RxTrafficHigh: ").append(isRxTrafficHigh) 558 .append(", Throughput Sufficient: ").append(isThroughputSufficient) 559 .toString()); 560 return isThroughputSufficient; 561 } 562 563 /** 564 * L2 tput is sufficient when one of the following conditions is met 565 * 1) L3 tput is low and L2 tput is above its low threshold 566 * 2) L3 tput is not low and L2 tput over L3 tput ratio is above sufficientRatioThr 567 * 3) L3 tput is not low and L2 tput is above its high threshold 568 * 4) L2 tput is invalid 569 */ isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput)570 private boolean isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput) { 571 if (l2TputKbps == INVALID_THROUGHPUT) return true; 572 int tputSufficientLowThrKbps = mDeviceConfigFacade.getTxTputSufficientLowThrKbps(); 573 int tputSufficientHighThrKbps = mDeviceConfigFacade.getTxTputSufficientHighThrKbps(); 574 if (isForRxTput) { 575 tputSufficientLowThrKbps = mDeviceConfigFacade.getRxTputSufficientLowThrKbps(); 576 tputSufficientHighThrKbps = mDeviceConfigFacade.getRxTputSufficientHighThrKbps(); 577 } 578 boolean isL3TputLow = (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum()) 579 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen()); 580 boolean isL2TputAboveLowThr = l2TputKbps >= tputSufficientLowThrKbps; 581 if (isL3TputLow) return isL2TputAboveLowThr; 582 583 boolean isL2TputAboveHighThr = l2TputKbps >= tputSufficientHighThrKbps; 584 boolean isL2L3TputRatioAboveThr = 585 (l2TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen()) 586 >= (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum()); 587 return isL2TputAboveHighThr || isL2L3TputRatioAboveThr; 588 } 589 detectAndOverrideFalseInSufficient(boolean isTputSufficient, boolean isTrafficHigh, boolean lastIsTputSufficient)590 private boolean detectAndOverrideFalseInSufficient(boolean isTputSufficient, 591 boolean isTrafficHigh, boolean lastIsTputSufficient) { 592 boolean possibleFalseInsufficient = (!isTrafficHigh && !isTputSufficient); 593 return possibleFalseInsufficient ? lastIsTputSufficient : isTputSufficient; 594 } 595 logd(String string)596 private void logd(String string) { 597 if (mVerboseLoggingEnabled) { 598 Log.d(TAG, string, null); 599 } 600 } 601 } 602