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