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.os.HandlerExecutor; 29 import android.telephony.PhoneStateListener; 30 import android.telephony.SubscriptionManager; 31 import android.telephony.TelephonyManager; 32 import android.util.Log; 33 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 import com.android.wifi.resources.R; 39 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 43 /** 44 * Looks for Wifi data stalls 45 */ 46 public class WifiDataStall { 47 private static final String TAG = "WifiDataStall"; 48 private boolean mVerboseLoggingEnabled = false; 49 public static final int INVALID_THROUGHPUT = -1; 50 // Maximum time gap between two WifiLinkLayerStats to trigger a data stall 51 public static final int MAX_MS_DELTA_FOR_DATA_STALL = 60 * 1000; // 1 minute 52 // Maximum time that a data stall start time stays valid. 53 public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes 54 // Default Tx packet error rate when there is no Tx attempt 55 public static final int DEFAULT_TX_PACKET_ERROR_RATE = 5; 56 // Default CCA level when CCA stats are not available 57 public static final int DEFAULT_CCA_LEVEL_2G = CHANNEL_UTILIZATION_SCALE * 16 / 100; 58 public static final int DEFAULT_CCA_LEVEL_ABOVE_2G = CHANNEL_UTILIZATION_SCALE * 6 / 100; 59 // Minimum time interval in ms between two link layer stats cache updates 60 private static final int LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS = 30_000; 61 // Maximum time margin between two link layer stats for connection duration update 62 public static final int MAX_TIME_MARGIN_LAST_TWO_POLLS_MS = 200; 63 64 private final FrameworkFacade mFacade; 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 74 private int mLastFrequency = -1; 75 private String mLastBssid; 76 private long mDataStallStartTimeMs = -1; 77 private Clock mClock; 78 private boolean mDataStallTx = false; 79 private boolean mDataStallRx = false; 80 private long mLastTxBytes; 81 private long mLastRxBytes; 82 private boolean mIsThroughputSufficient = true; 83 private boolean mIsCellularDataAvailable = false; 84 private final PhoneStateListener mPhoneStateListener; 85 private boolean mPhoneStateListenerEnabled = false; 86 private int mTxTputKbps = INVALID_THROUGHPUT; 87 private int mRxTputKbps = INVALID_THROUGHPUT; 88 89 /** @hide */ 90 @IntDef(prefix = { "CELLULAR_DATA_" }, value = { 91 CELLULAR_DATA_UNKNOWN, 92 CELLULAR_DATA_AVAILABLE, 93 CELLULAR_DATA_NOT_AVAILABLE, 94 }) 95 @Retention(RetentionPolicy.SOURCE) 96 public @interface CellularDataStatusCode {} 97 public static final int CELLULAR_DATA_UNKNOWN = 0; 98 public static final int CELLULAR_DATA_AVAILABLE = 1; 99 public static final int CELLULAR_DATA_NOT_AVAILABLE = 2; 100 101 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 102 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)103 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 104 update(); 105 } 106 107 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)108 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 109 update(); 110 } 111 112 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)113 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 114 update(); 115 } 116 update()117 private void update() { 118 // Register/Unregister phone listener on wifi on/off. 119 if (mActiveModeWarden.getPrimaryClientModeManagerNullable() != null) { 120 enablePhoneStateListener(); 121 } else { 122 disablePhoneStateListener(); 123 } 124 } 125 } 126 127 private class PrimaryModeChangeCallback implements PrimaryClientModeManagerChangedCallback { 128 @Override onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)129 public void onChange( 130 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager, 131 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) { 132 // This is needed to reset state on an MBB switch or wifi toggle. 133 if (prevPrimaryClientModeManager != null) { 134 reset(); 135 } 136 if (newPrimaryClientModeManager != null) { 137 init(); 138 } 139 } 140 } 141 142 private class ClientModeImplListenerInternal implements ClientModeImplListener { 143 @Override onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)144 public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) { 145 if (clientModeManager.getRole() == ActiveModeManager.ROLE_CLIENT_PRIMARY) { 146 reset(); 147 } 148 } 149 } 150 WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context, DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, Clock clock, Handler handler, ThroughputPredictor throughputPredictor, ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor)151 public WifiDataStall(FrameworkFacade facade, WifiMetrics wifiMetrics, Context context, 152 DeviceConfigFacade deviceConfigFacade, WifiChannelUtilization wifiChannelUtilization, 153 Clock clock, Handler handler, ThroughputPredictor throughputPredictor, 154 ActiveModeWarden activeModeWarden, ClientModeImplMonitor clientModeImplMonitor) { 155 mFacade = facade; 156 mDeviceConfigFacade = deviceConfigFacade; 157 mWifiMetrics = wifiMetrics; 158 mContext = context; 159 mClock = clock; 160 mWifiChannelUtilization = wifiChannelUtilization; 161 mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS); 162 mThroughputPredictor = throughputPredictor; 163 mActiveModeWarden = activeModeWarden; 164 mClientModeImplMonitor = clientModeImplMonitor; 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)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 int currFrequency = wifiInfo.getFrequency(); 328 mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency); 329 int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency); 330 mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency); 331 if (oldStats == null || newStats == null) { 332 // First poll after new association 333 // Update throughput with prediction 334 if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && connectionCapabilities != null) { 335 mTxTputKbps = mThroughputPredictor.predictTxThroughput(connectionCapabilities, 336 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 337 mRxTputKbps = mThroughputPredictor.predictRxThroughput(connectionCapabilities, 338 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 339 } 340 mIsThroughputSufficient = true; 341 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 342 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 343 return WifiIsUnusableEvent.TYPE_UNKNOWN; 344 } 345 346 long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk 347 + newStats.txmpdu_vi + newStats.txmpdu_vo) 348 - (oldStats.txmpdu_be + oldStats.txmpdu_bk 349 + oldStats.txmpdu_vi + oldStats.txmpdu_vo); 350 long txRetriesDelta = (newStats.retries_be + newStats.retries_bk 351 + newStats.retries_vi + newStats.retries_vo) 352 - (oldStats.retries_be + oldStats.retries_bk 353 + oldStats.retries_vi + oldStats.retries_vo); 354 long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk 355 + newStats.lostmpdu_vi + newStats.lostmpdu_vo) 356 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk 357 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo); 358 long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk 359 + newStats.rxmpdu_vi + newStats.rxmpdu_vo) 360 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk 361 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo); 362 int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs); 363 364 long totalTxDelta = txSuccessDelta + txRetriesDelta; 365 boolean isTxTrafficHigh = (totalTxDelta * 1000) 366 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 367 boolean isRxTrafficHigh = (rxSuccessDelta * 1000) 368 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 369 if (timeDeltaLastTwoPollsMs < 0 370 || txSuccessDelta < 0 371 || txRetriesDelta < 0 372 || txBadDelta < 0 373 || rxSuccessDelta < 0) { 374 mIsThroughputSufficient = true; 375 // There was a reset in WifiLinkLayerStats 376 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 377 return WifiIsUnusableEvent.TYPE_UNKNOWN; 378 } 379 380 mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, 381 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs); 382 383 int txLinkSpeedMbps = wifiInfo.getLinkSpeed(); 384 int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps(); 385 boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 386 || (mLastBssid.equals(wifiInfo.getBSSID()) 387 && mLastFrequency == currFrequency); 388 mLastFrequency = currFrequency; 389 mLastBssid = wifiInfo.getBSSID(); 390 391 if (ccaLevel == BssLoad.INVALID) { 392 ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G; 393 logd(" use default cca Level"); 394 } 395 logd(" ccaLevel = " + ccaLevel); 396 397 int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq, 398 isTxTrafficHigh); 399 400 boolean isTxTputLow = false; 401 boolean isRxTputLow = false; 402 403 if (txLinkSpeedMbps > 0) { 404 // Exclude update with low rate management frames 405 if (isTxTrafficHigh 406 || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) { 407 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100 408 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 409 } 410 isTxTputLow = mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps(); 411 } else { 412 mTxTputKbps = INVALID_THROUGHPUT; 413 } 414 415 if (rxLinkSpeedMbps > 0) { 416 // Exclude update with low rate management frames 417 if (isRxTrafficHigh 418 || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) { 419 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000 420 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 421 } 422 isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps(); 423 } else { 424 mRxTputKbps = INVALID_THROUGHPUT; 425 } 426 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 427 428 mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps, 429 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs); 430 431 int maxTimeDeltaMs = mContext.getResources().getInteger( 432 R.integer.config_wifiPollRssiIntervalMilliseconds) 433 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS; 434 if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) { 435 mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs, 436 mIsThroughputSufficient, mIsCellularDataAvailable); 437 } 438 439 boolean possibleDataStallTx = isTxTputLow 440 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr() 441 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr(); 442 boolean possibleDataStallRx = isRxTputLow 443 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr(); 444 445 boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx; 446 boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx; 447 448 return detectConsecutiveTwoDataStalls(ifaceName, timeDeltaLastTwoPollsMs, dataStallTx, 449 dataStallRx); 450 } 451 452 // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls 453 // 1st data stall should be preceded by no data stall 454 // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)455 private int detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, 456 boolean dataStallTx, boolean dataStallRx) { 457 if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) { 458 return WifiIsUnusableEvent.TYPE_UNKNOWN; 459 } 460 461 if (dataStallTx || dataStallRx) { 462 mDataStallTx = mDataStallTx || dataStallTx; 463 mDataStallRx = mDataStallRx || dataStallRx; 464 if (mDataStallStartTimeMs == -1) { 465 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); 466 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) { 467 mDataStallStartTimeMs = -1; 468 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 469 mDataStallRx); 470 mDataStallRx = false; 471 mDataStallTx = false; 472 return result; 473 } 474 } else { 475 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; 476 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) { 477 mDataStallStartTimeMs = -1; 478 if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { 479 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 480 mDataStallRx); 481 mDataStallRx = false; 482 mDataStallTx = false; 483 return result; 484 } else { 485 mDataStallTx = false; 486 mDataStallRx = false; 487 } 488 } else { 489 // No need to do anything. 490 } 491 } 492 } else { 493 mDataStallStartTimeMs = -1; 494 mDataStallTx = false; 495 mDataStallRx = false; 496 } 497 return WifiIsUnusableEvent.TYPE_UNKNOWN; 498 } 499 updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)500 private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, 501 boolean isTxTrafficHigh) { 502 if (!isSameBssidAndFreq) { 503 return DEFAULT_TX_PACKET_ERROR_RATE; 504 } 505 long txAttempts = txSuccessDelta + txRetriesDelta; 506 if (txAttempts <= 0 || !isTxTrafficHigh) { 507 return DEFAULT_TX_PACKET_ERROR_RATE; 508 } 509 return (int) (txRetriesDelta * 100 / txAttempts); 510 } calculateUsabilityEventType(String ifaceName, boolean dataStallTx, boolean dataStallRx)511 private int calculateUsabilityEventType(String ifaceName, boolean dataStallTx, 512 boolean dataStallRx) { 513 int result = WifiIsUnusableEvent.TYPE_UNKNOWN; 514 if (dataStallTx && dataStallRx) { 515 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; 516 } else if (dataStallTx) { 517 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; 518 } else if (dataStallRx) { 519 result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; 520 } 521 mWifiMetrics.logWifiIsUnusableEvent(ifaceName, result); 522 return result; 523 } 524 isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs)525 private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, 526 boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs) { 527 long txBytes = mFacade.getTotalTxBytes() - mFacade.getMobileTxBytes(); 528 long rxBytes = mFacade.getTotalRxBytes() - mFacade.getMobileRxBytes(); 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.getTputSufficientRatioThrDen()) 579 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum()); 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); 599 } 600 } 601 } 602