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 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 DeviceConfigFacade mDeviceConfigFacade; 65 private final WifiMetrics mWifiMetrics; 66 private final Context mContext; 67 private final WifiChannelUtilization mWifiChannelUtilization; 68 private TelephonyManager mTelephonyManager; 69 private final ThroughputPredictor mThroughputPredictor; 70 private final ActiveModeWarden mActiveModeWarden; 71 private final ClientModeImplMonitor mClientModeImplMonitor; 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)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 mDeviceConfigFacade = deviceConfigFacade; 155 mWifiMetrics = wifiMetrics; 156 mContext = context; 157 mClock = clock; 158 mWifiChannelUtilization = wifiChannelUtilization; 159 mWifiChannelUtilization.setCacheUpdateIntervalMs(LLSTATS_CACHE_UPDATE_INTERVAL_MIN_MS); 160 mThroughputPredictor = throughputPredictor; 161 mActiveModeWarden = activeModeWarden; 162 mClientModeImplMonitor = clientModeImplMonitor; 163 mPhoneStateListener = new PhoneStateListener(new HandlerExecutor(handler)) { 164 @Override 165 public void onDataConnectionStateChanged(int state, int networkType) { 166 if (state != TelephonyManager.DATA_CONNECTED 167 && state != TelephonyManager.DATA_DISCONNECTED) { 168 Log.e(TAG, "onDataConnectionStateChanged unexpected State: " + state); 169 return; 170 } 171 mIsCellularDataAvailable = state == TelephonyManager.DATA_CONNECTED; 172 mActiveModeWarden.getPrimaryClientModeManager() 173 .onCellularConnectivityChanged(mIsCellularDataAvailable 174 ? CELLULAR_DATA_AVAILABLE : CELLULAR_DATA_NOT_AVAILABLE); 175 logd("Cellular Data: " + mIsCellularDataAvailable); 176 } 177 }; 178 mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback( 179 new PrimaryModeChangeCallback()); 180 mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 181 mClientModeImplMonitor.registerListener(new ClientModeImplListenerInternal()); 182 } 183 184 /** 185 * initialization after wifi is enabled 186 */ init()187 private void init() { 188 mWifiChannelUtilization.init(null); 189 reset(); 190 } 191 192 /** 193 * Reset internal variables 194 */ reset()195 private void reset() { 196 mLastTxBytes = 0; 197 mLastRxBytes = 0; 198 mLastFrequency = -1; 199 mLastBssid = null; 200 mDataStallStartTimeMs = -1; 201 mDataStallTx = false; 202 mDataStallRx = false; 203 mIsThroughputSufficient = true; 204 mTxTputKbps = INVALID_THROUGHPUT; 205 mRxTputKbps = INVALID_THROUGHPUT; 206 } 207 createTelephonyManagerForDefaultDataSubIfNeeded()208 private void createTelephonyManagerForDefaultDataSubIfNeeded() { 209 if (mTelephonyManager == null) { 210 mTelephonyManager = (TelephonyManager) mContext 211 .getSystemService(Context.TELEPHONY_SERVICE); 212 } 213 int defaultSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); 214 if (defaultSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 215 && defaultSubscriptionId != mTelephonyManager.getSubscriptionId()) { 216 mTelephonyManager = mTelephonyManager.createForSubscriptionId( 217 SubscriptionManager.getDefaultDataSubscriptionId()); 218 } 219 } 220 221 /** 222 * Reset the PhoneStateListener to listen on the default data SIM. 223 */ resetPhoneStateListener()224 public void resetPhoneStateListener() { 225 disablePhoneStateListener(); 226 mActiveModeWarden.getPrimaryClientModeManager() 227 .onCellularConnectivityChanged(CELLULAR_DATA_UNKNOWN); 228 enablePhoneStateListener(); 229 } 230 231 /** 232 * Enable phone state listener 233 */ enablePhoneStateListener()234 private void enablePhoneStateListener() { 235 createTelephonyManagerForDefaultDataSubIfNeeded(); 236 if (mTelephonyManager != null && !mPhoneStateListenerEnabled) { 237 mPhoneStateListenerEnabled = true; 238 mTelephonyManager.listen(mPhoneStateListener, 239 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); 240 } 241 } 242 243 /** 244 * Disable phone state listener 245 */ disablePhoneStateListener()246 private void disablePhoneStateListener() { 247 if (mTelephonyManager != null && mPhoneStateListenerEnabled) { 248 mPhoneStateListenerEnabled = false; 249 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 250 } 251 } 252 253 /** 254 * Enable/Disable verbose logging. 255 * @param verbose true to enable and false to disable. 256 */ enableVerboseLogging(boolean verbose)257 public void enableVerboseLogging(boolean verbose) { 258 mVerboseLoggingEnabled = verbose; 259 mWifiChannelUtilization.enableVerboseLogging(verbose); 260 } 261 262 /** 263 * Update device mobility state 264 * @param newState the new device mobility state 265 */ setDeviceMobilityState(@eviceMobilityState int newState)266 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 267 mWifiChannelUtilization.setDeviceMobilityState(newState); 268 } 269 270 /** 271 * Check if current link layer throughput is sufficient. 272 * This should be called after checkDataStallAndThroughputSufficiency(). 273 * @return true if it is sufficient or false if it is insufficient 274 */ isThroughputSufficient()275 public boolean isThroughputSufficient() { 276 return mIsThroughputSufficient; 277 } 278 279 /** 280 * Check if cellular data is available 281 * @return true if it is available and false otherwise 282 */ isCellularDataAvailable()283 public boolean isCellularDataAvailable() { 284 return mIsCellularDataAvailable; 285 } 286 287 /** 288 * Get the latest Tx throughput based on Tx link speed, PER and channel utilization 289 * @return the latest estimated Tx throughput in Kbps if it is available 290 * or INVALID_THROUGHPUT if it is not available 291 */ getTxThroughputKbps()292 public int getTxThroughputKbps() { 293 logd("tx tput in kbps: " + mTxTputKbps); 294 return mTxTputKbps; 295 } 296 297 /** 298 * Get the latest Rx throughput based on Rx link speed and channel utilization 299 * @return the latest estimated Rx throughput in Kbps if it is available 300 * or INVALID_THROUGHPUT if it is not available 301 */ getRxThroughputKbps()302 public int getRxThroughputKbps() { 303 logd("rx tput in kbps: " + mRxTputKbps); 304 return mRxTputKbps; 305 } 306 307 /** 308 * Update data stall detection, check throughput sufficiency and report wifi health stat 309 * with the latest link layer stats 310 * @param connectionCapabilities Connection capabilities. 311 * @param oldStats second most recent WifiLinkLayerStats 312 * @param newStats most recent WifiLinkLayerStats 313 * @param wifiInfo WifiInfo for current connection 314 * @return trigger type of WifiIsUnusableEvent 315 * 316 * Note: This is only collected for primary STA currently because RSSI polling is disabled for 317 * non-primary STAs. 318 */ checkDataStallAndThroughputSufficiency( @onNull String ifaceName, @NonNull ConnectionCapabilities connectionCapabilities, @Nullable WifiLinkLayerStats oldStats, @Nullable WifiLinkLayerStats newStats, @NonNull WifiInfo wifiInfo, long txBytes, long rxBytes)319 public int checkDataStallAndThroughputSufficiency( 320 @NonNull String ifaceName, 321 @NonNull ConnectionCapabilities connectionCapabilities, 322 @Nullable WifiLinkLayerStats oldStats, 323 @Nullable WifiLinkLayerStats newStats, 324 @NonNull WifiInfo wifiInfo, 325 long txBytes, long rxBytes) { 326 int currFrequency = wifiInfo.getFrequency(); 327 mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency); 328 int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency); 329 mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency); 330 if (oldStats == null || newStats == null) { 331 // First poll after new association 332 // Update throughput with prediction 333 if (wifiInfo.getRssi() != WifiInfo.INVALID_RSSI && connectionCapabilities != null) { 334 mTxTputKbps = mThroughputPredictor.predictTxThroughput(connectionCapabilities, 335 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 336 mRxTputKbps = mThroughputPredictor.predictRxThroughput(connectionCapabilities, 337 wifiInfo.getRssi(), currFrequency, ccaLevel) * 1000; 338 } 339 mIsThroughputSufficient = true; 340 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 341 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 342 return WifiIsUnusableEvent.TYPE_UNKNOWN; 343 } 344 345 long txSuccessDelta = (newStats.txmpdu_be + newStats.txmpdu_bk 346 + newStats.txmpdu_vi + newStats.txmpdu_vo) 347 - (oldStats.txmpdu_be + oldStats.txmpdu_bk 348 + oldStats.txmpdu_vi + oldStats.txmpdu_vo); 349 long txRetriesDelta = (newStats.retries_be + newStats.retries_bk 350 + newStats.retries_vi + newStats.retries_vo) 351 - (oldStats.retries_be + oldStats.retries_bk 352 + oldStats.retries_vi + oldStats.retries_vo); 353 long txBadDelta = (newStats.lostmpdu_be + newStats.lostmpdu_bk 354 + newStats.lostmpdu_vi + newStats.lostmpdu_vo) 355 - (oldStats.lostmpdu_be + oldStats.lostmpdu_bk 356 + oldStats.lostmpdu_vi + oldStats.lostmpdu_vo); 357 long rxSuccessDelta = (newStats.rxmpdu_be + newStats.rxmpdu_bk 358 + newStats.rxmpdu_vi + newStats.rxmpdu_vo) 359 - (oldStats.rxmpdu_be + oldStats.rxmpdu_bk 360 + oldStats.rxmpdu_vi + oldStats.rxmpdu_vo); 361 int timeDeltaLastTwoPollsMs = (int) (newStats.timeStampInMs - oldStats.timeStampInMs); 362 363 long totalTxDelta = txSuccessDelta + txRetriesDelta; 364 boolean isTxTrafficHigh = (totalTxDelta * 1000) 365 > (mDeviceConfigFacade.getTxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 366 boolean isRxTrafficHigh = (rxSuccessDelta * 1000) 367 > (mDeviceConfigFacade.getRxPktPerSecondThr() * timeDeltaLastTwoPollsMs); 368 if (timeDeltaLastTwoPollsMs < 0 369 || txSuccessDelta < 0 370 || txRetriesDelta < 0 371 || txBadDelta < 0 372 || rxSuccessDelta < 0) { 373 mIsThroughputSufficient = true; 374 // There was a reset in WifiLinkLayerStats 375 mWifiMetrics.resetWifiIsUnusableLinkLayerStats(); 376 return WifiIsUnusableEvent.TYPE_UNKNOWN; 377 } 378 379 mWifiMetrics.updateWifiIsUnusableLinkLayerStats(txSuccessDelta, txRetriesDelta, 380 txBadDelta, rxSuccessDelta, timeDeltaLastTwoPollsMs); 381 382 int txLinkSpeedMbps = wifiInfo.getLinkSpeed(); 383 int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps(); 384 boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 385 || (mLastBssid.equals(wifiInfo.getBSSID()) 386 && mLastFrequency == currFrequency); 387 mLastFrequency = currFrequency; 388 mLastBssid = wifiInfo.getBSSID(); 389 390 if (ccaLevel == BssLoad.INVALID) { 391 ccaLevel = wifiInfo.is24GHz() ? DEFAULT_CCA_LEVEL_2G : DEFAULT_CCA_LEVEL_ABOVE_2G; 392 logd(" use default cca Level"); 393 } 394 logd(" ccaLevel = " + ccaLevel); 395 396 int txPer = updateTxPer(txSuccessDelta, txRetriesDelta, isSameBssidAndFreq, 397 isTxTrafficHigh); 398 399 boolean isTxTputLow = false; 400 boolean isRxTputLow = false; 401 402 if (txLinkSpeedMbps > 0) { 403 // Exclude update with low rate management frames 404 if (isTxTrafficHigh 405 || txLinkSpeedMbps > mDeviceConfigFacade.getTxLinkSpeedLowThresholdMbps()) { 406 mTxTputKbps = (int) ((long) txLinkSpeedMbps * 1000 * (100 - txPer) / 100 407 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 408 } 409 isTxTputLow = mTxTputKbps < mDeviceConfigFacade.getDataStallTxTputThrKbps(); 410 } else { 411 mTxTputKbps = INVALID_THROUGHPUT; 412 } 413 414 if (rxLinkSpeedMbps > 0) { 415 // Exclude update with low rate management frames 416 if (isRxTrafficHigh 417 || rxLinkSpeedMbps > mDeviceConfigFacade.getRxLinkSpeedLowThresholdMbps()) { 418 mRxTputKbps = (int) ((long) rxLinkSpeedMbps * 1000 419 * (CHANNEL_UTILIZATION_SCALE - ccaLevel) / CHANNEL_UTILIZATION_SCALE); 420 } 421 isRxTputLow = mRxTputKbps < mDeviceConfigFacade.getDataStallRxTputThrKbps(); 422 } else { 423 mRxTputKbps = INVALID_THROUGHPUT; 424 } 425 mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency); 426 427 mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps, 428 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs, txBytes, rxBytes); 429 430 int maxTimeDeltaMs = mContext.getResources().getInteger( 431 R.integer.config_wifiPollRssiIntervalMilliseconds) 432 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS; 433 if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) { 434 mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs, 435 mIsThroughputSufficient, mIsCellularDataAvailable); 436 } 437 438 boolean possibleDataStallTx = isTxTputLow 439 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr() 440 || txPer >= mDeviceConfigFacade.getDataStallTxPerThr(); 441 boolean possibleDataStallRx = isRxTputLow 442 || ccaLevel >= mDeviceConfigFacade.getDataStallCcaLevelThr(); 443 444 boolean dataStallTx = isTxTrafficHigh ? possibleDataStallTx : mDataStallTx; 445 boolean dataStallRx = isRxTrafficHigh ? possibleDataStallRx : mDataStallRx; 446 447 return detectConsecutiveTwoDataStalls(ifaceName, timeDeltaLastTwoPollsMs, dataStallTx, 448 dataStallRx); 449 } 450 451 // Data stall event is triggered if there are consecutive Tx and/or Rx data stalls 452 // 1st data stall should be preceded by no data stall 453 // Reset mDataStallStartTimeMs to -1 if currently there is no Tx or Rx data stall detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, boolean dataStallTx, boolean dataStallRx)454 private int detectConsecutiveTwoDataStalls(String ifaceName, int timeDeltaLastTwoPollsMs, 455 boolean dataStallTx, boolean dataStallRx) { 456 if (timeDeltaLastTwoPollsMs >= MAX_MS_DELTA_FOR_DATA_STALL) { 457 return WifiIsUnusableEvent.TYPE_UNKNOWN; 458 } 459 460 if (dataStallTx || dataStallRx) { 461 mDataStallTx = mDataStallTx || dataStallTx; 462 mDataStallRx = mDataStallRx || dataStallRx; 463 if (mDataStallStartTimeMs == -1) { 464 mDataStallStartTimeMs = mClock.getElapsedSinceBootMillis(); 465 if (mDeviceConfigFacade.getDataStallDurationMs() == 0) { 466 mDataStallStartTimeMs = -1; 467 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 468 mDataStallRx); 469 mDataStallRx = false; 470 mDataStallTx = false; 471 return result; 472 } 473 } else { 474 long elapsedTime = mClock.getElapsedSinceBootMillis() - mDataStallStartTimeMs; 475 if (elapsedTime >= mDeviceConfigFacade.getDataStallDurationMs()) { 476 mDataStallStartTimeMs = -1; 477 if (elapsedTime <= VALIDITY_PERIOD_OF_DATA_STALL_START_MS) { 478 int result = calculateUsabilityEventType(ifaceName, mDataStallTx, 479 mDataStallRx); 480 mDataStallRx = false; 481 mDataStallTx = false; 482 return result; 483 } else { 484 mDataStallTx = false; 485 mDataStallRx = false; 486 } 487 } else { 488 // No need to do anything. 489 } 490 } 491 } else { 492 mDataStallStartTimeMs = -1; 493 mDataStallTx = false; 494 mDataStallRx = false; 495 } 496 return WifiIsUnusableEvent.TYPE_UNKNOWN; 497 } 498 updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, boolean isTxTrafficHigh)499 private int updateTxPer(long txSuccessDelta, long txRetriesDelta, boolean isSameBssidAndFreq, 500 boolean isTxTrafficHigh) { 501 if (!isSameBssidAndFreq) { 502 return DEFAULT_TX_PACKET_ERROR_RATE; 503 } 504 long txAttempts = txSuccessDelta + txRetriesDelta; 505 if (txAttempts <= 0 || !isTxTrafficHigh) { 506 return DEFAULT_TX_PACKET_ERROR_RATE; 507 } 508 return (int) (txRetriesDelta * 100 / txAttempts); 509 } calculateUsabilityEventType(String ifaceName, boolean dataStallTx, boolean dataStallRx)510 private int calculateUsabilityEventType(String ifaceName, boolean dataStallTx, 511 boolean dataStallRx) { 512 int result = WifiIsUnusableEvent.TYPE_UNKNOWN; 513 if (dataStallTx && dataStallRx) { 514 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH; 515 } else if (dataStallTx) { 516 result = WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX; 517 } else if (dataStallRx) { 518 result = WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX; 519 } 520 mWifiMetrics.logWifiIsUnusableEvent(ifaceName, result); 521 return result; 522 } 523 isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs, long txBytes, long rxBytes)524 private boolean isThroughputSufficientInternal(int l2TxTputKbps, int l2RxTputKbps, 525 boolean isTxTrafficHigh, boolean isRxTrafficHigh, int timeDeltaLastTwoPollsMs, 526 long txBytes, long rxBytes) { 527 if (timeDeltaLastTwoPollsMs > MAX_MS_DELTA_FOR_DATA_STALL 528 || mLastTxBytes == 0 || mLastRxBytes == 0) { 529 mLastTxBytes = txBytes; 530 mLastRxBytes = rxBytes; 531 return true; 532 } 533 534 int l3TxTputKbps = (int) ((txBytes - mLastTxBytes) * 8 / timeDeltaLastTwoPollsMs); 535 int l3RxTputKbps = (int) ((rxBytes - mLastRxBytes) * 8 / timeDeltaLastTwoPollsMs); 536 537 mLastTxBytes = txBytes; 538 mLastRxBytes = rxBytes; 539 540 boolean isTxTputSufficient = isL2ThroughputSufficient(l2TxTputKbps, l3TxTputKbps, false); 541 boolean isRxTputSufficient = isL2ThroughputSufficient(l2RxTputKbps, l3RxTputKbps, true); 542 isTxTputSufficient = detectAndOverrideFalseInSufficient( 543 isTxTputSufficient, isTxTrafficHigh, mIsThroughputSufficient); 544 isRxTputSufficient = detectAndOverrideFalseInSufficient( 545 isRxTputSufficient, isRxTrafficHigh, mIsThroughputSufficient); 546 547 boolean isThroughputSufficient = isTxTputSufficient && isRxTputSufficient; 548 549 StringBuilder sb = new StringBuilder(); 550 logd(sb.append("L2 txTputKbps: ").append(l2TxTputKbps) 551 .append(", rxTputKbps: ").append(l2RxTputKbps) 552 .append(", L3 txTputKbps: ").append(l3TxTputKbps) 553 .append(", rxTputKbps: ").append(l3RxTputKbps) 554 .append(", TxTrafficHigh: ").append(isTxTrafficHigh) 555 .append(", RxTrafficHigh: ").append(isRxTrafficHigh) 556 .append(", Throughput Sufficient: ").append(isThroughputSufficient) 557 .toString()); 558 return isThroughputSufficient; 559 } 560 561 /** 562 * L2 tput is sufficient when one of the following conditions is met 563 * 1) L3 tput is low and L2 tput is above its low threshold 564 * 2) L3 tput is not low and L2 tput over L3 tput ratio is above sufficientRatioThr 565 * 3) L3 tput is not low and L2 tput is above its high threshold 566 * 4) L2 tput is invalid 567 */ isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput)568 private boolean isL2ThroughputSufficient(int l2TputKbps, int l3TputKbps, boolean isForRxTput) { 569 if (l2TputKbps == INVALID_THROUGHPUT) return true; 570 int tputSufficientLowThrKbps = mDeviceConfigFacade.getTxTputSufficientLowThrKbps(); 571 int tputSufficientHighThrKbps = mDeviceConfigFacade.getTxTputSufficientHighThrKbps(); 572 if (isForRxTput) { 573 tputSufficientLowThrKbps = mDeviceConfigFacade.getRxTputSufficientLowThrKbps(); 574 tputSufficientHighThrKbps = mDeviceConfigFacade.getRxTputSufficientHighThrKbps(); 575 } 576 boolean isL3TputLow = (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen()) 577 < (tputSufficientLowThrKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum()); 578 boolean isL2TputAboveLowThr = l2TputKbps >= tputSufficientLowThrKbps; 579 if (isL3TputLow) return isL2TputAboveLowThr; 580 581 boolean isL2TputAboveHighThr = l2TputKbps >= tputSufficientHighThrKbps; 582 boolean isL2L3TputRatioAboveThr = 583 (l2TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrDen()) 584 >= (l3TputKbps * mDeviceConfigFacade.getTputSufficientRatioThrNum()); 585 return isL2TputAboveHighThr || isL2L3TputRatioAboveThr; 586 } 587 detectAndOverrideFalseInSufficient(boolean isTputSufficient, boolean isTrafficHigh, boolean lastIsTputSufficient)588 private boolean detectAndOverrideFalseInSufficient(boolean isTputSufficient, 589 boolean isTrafficHigh, boolean lastIsTputSufficient) { 590 boolean possibleFalseInsufficient = (!isTrafficHigh && !isTputSufficient); 591 return possibleFalseInsufficient ? lastIsTputSufficient : isTputSufficient; 592 } 593 logd(String string)594 private void logd(String string) { 595 if (mVerboseLoggingEnabled) { 596 Log.d(TAG, string); 597 } 598 } 599 } 600