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