1 /* 2 * Copyright 2019 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 package com.android.server.wifi; 17 18 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.INVALID; 19 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MAX_CHANNEL_UTILIZATION; 20 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MIN_CHANNEL_UTILIZATION; 21 22 import android.annotation.NonNull; 23 import android.content.Context; 24 import android.net.wifi.ScanResult; 25 import android.net.wifi.WifiAnnotations.WifiStandard; 26 import android.net.wifi.WifiInfo; 27 import android.net.wifi.nl80211.DeviceWiphyCapabilities; 28 import android.util.Log; 29 30 import com.android.wifi.resources.R; 31 32 /** 33 * A class that predicts network throughput based on RSSI, channel utilization, channel width, 34 * WiFi standard (PHY/MAC mode), Nss and other radio information. 35 */ 36 public class ThroughputPredictor { 37 private static final String TAG = "WifiThroughputPredictor"; 38 private boolean mVerboseLoggingEnabled = false; 39 40 // Default value of channel utilization at 2G when channel utilization is not available from 41 // BssLoad IE or from link layer stats 42 public static final int CHANNEL_UTILIZATION_DEFAULT_2G = MAX_CHANNEL_UTILIZATION * 6 / 16; 43 // Default value of channel utilization at 5G when channel utilization is not available from 44 // BssLoad IE or from link layer stats 45 public static final int CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G = MAX_CHANNEL_UTILIZATION / 16; 46 // Channel utilization boost when bluetooth is in the connected mode 47 public static final int CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G = MAX_CHANNEL_UTILIZATION / 4; 48 //TODO: b/145133625 Need to consider 6GHz 49 50 // Number of data tones per OFDM symbol 51 private static final int NUM_TONE_PER_SYM_LEGACY = 48; 52 private static final int NUM_TONE_PER_SYM_11N_20MHZ = 52; 53 private static final int NUM_TONE_PER_SYM_11N_40MHZ = 108; 54 private static final int NUM_TONE_PER_SYM_11AC_20MHZ = 52; 55 private static final int NUM_TONE_PER_SYM_11AC_40MHZ = 108; 56 private static final int NUM_TONE_PER_SYM_11AC_80MHZ = 234; 57 private static final int NUM_TONE_PER_SYM_11AC_160MHZ = 468; 58 private static final int NUM_TONE_PER_SYM_11AX_20MHZ = 234; 59 private static final int NUM_TONE_PER_SYM_11AX_40MHZ = 468; 60 private static final int NUM_TONE_PER_SYM_11AX_80MHZ = 980; 61 private static final int NUM_TONE_PER_SYM_11AX_160MHZ = 1960; 62 63 // 11ag OFDM symbol duration in ns 64 private static final int SYM_DURATION_LEGACY_NS = 4000; 65 // 11n OFDM symbol duration in ns with 0.4us guard interval 66 private static final int SYM_DURATION_11N_NS = 3600; 67 // 11ac OFDM symbol duration in ns with 0.4us guard interval 68 private static final int SYM_DURATION_11AC_NS = 3600; 69 // 11ax OFDM symbol duration in ns with 0.8us guard interval 70 private static final int SYM_DURATION_11AX_NS = 13600; 71 private static final int MICRO_TO_NANO_RATIO = 1000; 72 73 // The scaling factor for integer representation of bitPerTone and MAX_BITS_PER_TONE_XXX 74 private static final int BIT_PER_TONE_SCALE = 1000; 75 private static final int MAX_BITS_PER_TONE_LEGACY = 76 (int) Math.round((6 * 3.0 * BIT_PER_TONE_SCALE) / 4.0); 77 private static final int MAX_BITS_PER_TONE_11N = 78 (int) Math.round((6 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 79 private static final int MAX_BITS_PER_TONE_11AC = 80 (int) Math.round((8 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 81 private static final int MAX_BITS_PER_TONE_11AX = 82 (int) Math.round((10 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 83 84 // snrDb-to-bitPerTone lookup table (LUT) used at low SNR 85 // snr = Math.pow(10.0, snrDb / 10.0); 86 // bitPerTone = (int) (Math.log10(1 + snr) / Math.log10(2.0) * BIT_PER_TONE_SCALE) 87 private static final int TWO_IN_DB = 3; 88 private static final int SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / TWO_IN_DB; 89 private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MIN = -10; // minimum snrDb supported by LUT 90 private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MAX = 9; // maximum snrDb supported by LUT 91 private static final int[] SNR_DB_TO_BIT_PER_TONE_LUT = {0, 171, 212, 262, 323, 396, 484, 586, 92 706, 844, 1000, 1176, 1370, 1583, 1812, 2058, 2317, 2588, 2870, 3161}; 93 // Thermal noise floor power in dBm integrated over 20MHz with 5.5dB noise figure at 25C 94 private static final int NOISE_FLOOR_20MHZ_DBM = -96; 95 // A fudge factor to represent HW implementation margin in dB. 96 // Predicted throughput matches pretty well with OTA throughput with this fudge factor. 97 private static final int SNR_MARGIN_DB = 16; 98 private static final int MAX_NUM_SPATIAL_STREAM_11AX = 8; 99 private static final int MAX_NUM_SPATIAL_STREAM_11AC = 8; 100 private static final int MAX_NUM_SPATIAL_STREAM_11N = 4; 101 private static final int MAX_NUM_SPATIAL_STREAM_LEGACY = 1; 102 103 private static final int B_MODE_MAX_MBPS = 11; 104 private final Context mContext; 105 ThroughputPredictor(Context context)106 ThroughputPredictor(Context context) { 107 mContext = context; 108 } 109 110 /** 111 * Enable/Disable verbose logging. 112 * 113 * @param verbose true to enable and false to disable. 114 */ enableVerboseLogging(boolean verbose)115 public void enableVerboseLogging(boolean verbose) { 116 mVerboseLoggingEnabled = verbose; 117 } 118 119 /** 120 * Predict maximum Tx throughput supported by connected network at the highest RSSI 121 * with the lowest channel utilization 122 * @return predicted maximum Tx throughput in Mbps 123 */ predictMaxTxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities)124 public int predictMaxTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) { 125 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 126 capabilities.channelBandwidth, 127 WifiInfo.MAX_RSSI, capabilities.maxNumberTxSpatialStreams, MIN_CHANNEL_UTILIZATION); 128 } 129 130 /** 131 * Predict maximum Rx throughput supported by connected network at the highest RSSI 132 * with the lowest channel utilization 133 * @return predicted maximum Rx throughput in Mbps 134 */ predictMaxRxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities)135 public int predictMaxRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) { 136 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 137 capabilities.channelBandwidth, 138 WifiInfo.MAX_RSSI, capabilities.maxNumberRxSpatialStreams, MIN_CHANNEL_UTILIZATION); 139 } 140 141 /** 142 * Predict Tx throughput with current connection capabilities, RSSI and channel utilization 143 * @return predicted Tx throughput in Mbps 144 */ predictTxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization)145 public int predictTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, 146 int rssiDbm, int frequency, int channelUtilization) { 147 int channelUtilizationFinal = getValidChannelUtilization(frequency, 148 INVALID, channelUtilization, false); 149 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 150 capabilities.channelBandwidth, 151 rssiDbm, capabilities.maxNumberTxSpatialStreams, channelUtilizationFinal); 152 } 153 154 /** 155 * Predict Rx throughput with current connection capabilities, RSSI and channel utilization 156 * @return predicted Rx throughput in Mbps 157 */ predictRxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization)158 public int predictRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, 159 int rssiDbm, int frequency, int channelUtilization) { 160 int channelUtilizationFinal = getValidChannelUtilization(frequency, 161 INVALID, channelUtilization, false); 162 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 163 capabilities.channelBandwidth, 164 rssiDbm, capabilities.maxNumberRxSpatialStreams, channelUtilizationFinal); 165 } 166 167 /** 168 * Predict network throughput given by the current channel condition and RSSI 169 * @param deviceCapabilities Phy Capabilities of the device 170 * @param wifiStandardAp the highest wifi standard supported by AP 171 * @param channelWidthAp the channel bandwidth of AP 172 * @param rssiDbm the scan RSSI in dBm 173 * @param frequency the center frequency of primary 20MHz channel 174 * @param maxNumSpatialStreamAp the maximum number of spatial streams supported by AP 175 * @param channelUtilizationBssLoad the channel utilization ratio indicated from BssLoad IE 176 * @param channelUtilizationLinkLayerStats the channel utilization ratio detected from scan 177 * @param isBluetoothConnected whether the bluetooth adaptor is in connected mode 178 * @return predicted throughput in Mbps 179 */ predictThroughput(DeviceWiphyCapabilities deviceCapabilities, @WifiStandard int wifiStandardAp, int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp, int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected)180 public int predictThroughput(DeviceWiphyCapabilities deviceCapabilities, 181 @WifiStandard int wifiStandardAp, 182 int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp, 183 int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, 184 boolean isBluetoothConnected) { 185 186 if (deviceCapabilities == null) { 187 Log.e(TAG, "Null device capabilities passed to throughput predictor"); 188 return 0; 189 } 190 191 int maxNumSpatialStreamDevice = Math.min(deviceCapabilities.getMaxNumberTxSpatialStreams(), 192 deviceCapabilities.getMaxNumberRxSpatialStreams()); 193 194 if (mContext.getResources().getBoolean( 195 R.bool.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideEnable)) { 196 maxNumSpatialStreamDevice = mContext.getResources().getInteger( 197 R.integer.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideValue); 198 } 199 200 int maxNumSpatialStream = Math.min(maxNumSpatialStreamDevice, maxNumSpatialStreamAp); 201 202 // Get minimum standard support between device and AP 203 int wifiStandard; 204 switch (wifiStandardAp) { 205 case ScanResult.WIFI_STANDARD_11AX: 206 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AX)) { 207 wifiStandard = ScanResult.WIFI_STANDARD_11AX; 208 break; 209 } 210 //FALL THROUGH 211 case ScanResult.WIFI_STANDARD_11AC: 212 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AC)) { 213 wifiStandard = ScanResult.WIFI_STANDARD_11AC; 214 break; 215 } 216 //FALL THROUGH 217 case ScanResult.WIFI_STANDARD_11N: 218 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11N)) { 219 wifiStandard = ScanResult.WIFI_STANDARD_11N; 220 break; 221 } 222 //FALL THROUGH 223 default: 224 wifiStandard = ScanResult.WIFI_STANDARD_LEGACY; 225 } 226 227 // Calculate channel width 228 int channelWidth; 229 switch (channelWidthAp) { 230 case ScanResult.CHANNEL_WIDTH_160MHZ: 231 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_160MHZ)) { 232 channelWidth = ScanResult.CHANNEL_WIDTH_160MHZ; 233 break; 234 } 235 // FALL THROUGH 236 case ScanResult.CHANNEL_WIDTH_80MHZ: 237 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)) { 238 channelWidth = ScanResult.CHANNEL_WIDTH_80MHZ; 239 break; 240 } 241 // FALL THROUGH 242 case ScanResult.CHANNEL_WIDTH_40MHZ: 243 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)) { 244 channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ; 245 break; 246 } 247 // FALL THROUGH 248 default: 249 channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ; 250 } 251 252 if (mVerboseLoggingEnabled) { 253 StringBuilder sb = new StringBuilder(); 254 Log.d(TAG, sb.append("AP Nss: ").append(maxNumSpatialStreamAp) 255 .append(", Device Nss: ").append(maxNumSpatialStreamDevice) 256 .append(", freq: ").append(frequency) 257 .toString()); 258 } 259 260 int channelUtilization = getValidChannelUtilization(frequency, 261 channelUtilizationBssLoad, 262 channelUtilizationLinkLayerStats, 263 isBluetoothConnected); 264 265 return predictThroughputInternal(wifiStandard, false/* is11bMode */, channelWidth, 266 rssiDbm, maxNumSpatialStream, channelUtilization); 267 } 268 predictThroughputInternal(@ifiStandard int wifiStandard, boolean is11bMode, int channelWidth, int rssiDbm, int maxNumSpatialStream, int channelUtilization)269 private int predictThroughputInternal(@WifiStandard int wifiStandard, boolean is11bMode, 270 int channelWidth, int rssiDbm, int maxNumSpatialStream, int channelUtilization) { 271 272 // channel bandwidth in MHz = 20MHz * (2 ^ channelWidthFactor); 273 int channelWidthFactor; 274 int numTonePerSym; 275 int symDurationNs; 276 int maxBitsPerTone; 277 if (maxNumSpatialStream < 1) { 278 Log.e(TAG, "maxNumSpatialStream < 1 due to wrong implementation. Overridden to 1"); 279 maxNumSpatialStream = 1; 280 } 281 if (wifiStandard == ScanResult.WIFI_STANDARD_UNKNOWN) { 282 return WifiInfo.LINK_SPEED_UNKNOWN; 283 } else if (wifiStandard == ScanResult.WIFI_STANDARD_LEGACY) { 284 // For simplicity, use legacy OFDM parameters to predict 11b rate 285 numTonePerSym = NUM_TONE_PER_SYM_LEGACY; 286 channelWidthFactor = 0; 287 maxNumSpatialStream = MAX_NUM_SPATIAL_STREAM_LEGACY; 288 maxBitsPerTone = MAX_BITS_PER_TONE_LEGACY; 289 symDurationNs = SYM_DURATION_LEGACY_NS; 290 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11N) { 291 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 292 numTonePerSym = NUM_TONE_PER_SYM_11N_20MHZ; 293 channelWidthFactor = 0; 294 } else { 295 numTonePerSym = NUM_TONE_PER_SYM_11N_40MHZ; 296 channelWidthFactor = 1; 297 } 298 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11N); 299 maxBitsPerTone = MAX_BITS_PER_TONE_11N; 300 symDurationNs = SYM_DURATION_11N_NS; 301 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AC) { 302 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 303 numTonePerSym = NUM_TONE_PER_SYM_11AC_20MHZ; 304 channelWidthFactor = 0; 305 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { 306 numTonePerSym = NUM_TONE_PER_SYM_11AC_40MHZ; 307 channelWidthFactor = 1; 308 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { 309 numTonePerSym = NUM_TONE_PER_SYM_11AC_80MHZ; 310 channelWidthFactor = 2; 311 } else { 312 numTonePerSym = NUM_TONE_PER_SYM_11AC_160MHZ; 313 channelWidthFactor = 3; 314 } 315 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AC); 316 maxBitsPerTone = MAX_BITS_PER_TONE_11AC; 317 symDurationNs = SYM_DURATION_11AC_NS; 318 } else { // ScanResult.WIFI_STANDARD_11AX 319 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 320 numTonePerSym = NUM_TONE_PER_SYM_11AX_20MHZ; 321 channelWidthFactor = 0; 322 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { 323 numTonePerSym = NUM_TONE_PER_SYM_11AX_40MHZ; 324 channelWidthFactor = 1; 325 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { 326 numTonePerSym = NUM_TONE_PER_SYM_11AX_80MHZ; 327 channelWidthFactor = 2; 328 } else { 329 numTonePerSym = NUM_TONE_PER_SYM_11AX_160MHZ; 330 channelWidthFactor = 3; 331 } 332 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AX); 333 maxBitsPerTone = MAX_BITS_PER_TONE_11AX; 334 symDurationNs = SYM_DURATION_11AX_NS; 335 } 336 // noiseFloorDbBoost = 10 * log10 * (2 ^ channelWidthFactor) 337 int noiseFloorDbBoost = TWO_IN_DB * channelWidthFactor; 338 int noiseFloorDbm = NOISE_FLOOR_20MHZ_DBM + noiseFloorDbBoost + SNR_MARGIN_DB; 339 int snrDb = rssiDbm - noiseFloorDbm; 340 341 int bitPerTone = calculateBitPerTone(snrDb); 342 bitPerTone = Math.min(bitPerTone, maxBitsPerTone); 343 344 long bitPerToneTotal = bitPerTone * maxNumSpatialStream; 345 long numBitPerSym = bitPerToneTotal * numTonePerSym; 346 int phyRateMbps = (int) ((numBitPerSym * MICRO_TO_NANO_RATIO) 347 / (symDurationNs * BIT_PER_TONE_SCALE)); 348 349 int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor); 350 351 int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION; 352 353 if (is11bMode) { 354 throughputMbps = Math.min(throughputMbps, B_MODE_MAX_MBPS); 355 } 356 if (mVerboseLoggingEnabled) { 357 StringBuilder sb = new StringBuilder(); 358 Log.d(TAG, sb.append(" BW: ").append(channelWidth) 359 .append(" RSSI: ").append(rssiDbm) 360 .append(" Nss: ").append(maxNumSpatialStream) 361 .append(" Mode: ").append(wifiStandard) 362 .append(" symDur: ").append(symDurationNs) 363 .append(" snrDb ").append(snrDb) 364 .append(" bitPerTone: ").append(bitPerTone) 365 .append(" rate: ").append(phyRateMbps) 366 .append(" throughput: ").append(throughputMbps) 367 .toString()); 368 } 369 return throughputMbps; 370 } 371 372 // Calculate the number of bits per tone based on the input of SNR in dB 373 // The output is scaled up by BIT_PER_TONE_SCALE for integer representation calculateBitPerTone(int snrDb)374 private static int calculateBitPerTone(int snrDb) { 375 int bitPerTone; 376 if (snrDb <= SNR_DB_TO_BIT_PER_TONE_LUT_MAX) { 377 int lut_in_idx = Math.max(snrDb, SNR_DB_TO_BIT_PER_TONE_LUT_MIN) 378 - SNR_DB_TO_BIT_PER_TONE_LUT_MIN; 379 lut_in_idx = Math.min(lut_in_idx, SNR_DB_TO_BIT_PER_TONE_LUT.length - 1); 380 bitPerTone = SNR_DB_TO_BIT_PER_TONE_LUT[lut_in_idx]; 381 } else { 382 // bitPerTone = Math.log10(1+snr)/Math.log10(2) can be approximated as 383 // Math.log10(snr) / 0.3 = log10(10^(snrDb/10)) / 0.3 = snrDb / 3 384 // SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / 3 385 bitPerTone = snrDb * SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE; 386 } 387 return bitPerTone; 388 } 389 getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected)390 private int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, 391 int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) { 392 int channelUtilization; 393 boolean is2G = ScanResult.is24GHz(frequency); 394 if (isValidUtilizationRatio(channelUtilizationBssLoad)) { 395 channelUtilization = channelUtilizationBssLoad; 396 } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) { 397 channelUtilization = channelUtilizationLinkLayerStats; 398 } else { 399 channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G : 400 CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G; 401 } 402 403 if (is2G && isBluetoothConnected) { 404 channelUtilization += CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G; 405 channelUtilization = Math.min(channelUtilization, MAX_CHANNEL_UTILIZATION); 406 } 407 if (mVerboseLoggingEnabled) { 408 StringBuilder sb = new StringBuilder(); 409 Log.d(TAG, sb.append(" utilization (BssLoad) ").append(channelUtilizationBssLoad) 410 .append(" utilization (LLStats) ").append(channelUtilizationLinkLayerStats) 411 .append(" isBluetoothConnected: ").append(isBluetoothConnected) 412 .append(" final utilization: ").append(channelUtilization) 413 .toString()); 414 } 415 return channelUtilization; 416 } 417 418 /** 419 * Check if the channel utilization ratio is valid 420 */ isValidUtilizationRatio(int utilizationRatio)421 private static boolean isValidUtilizationRatio(int utilizationRatio) { 422 return (utilizationRatio <= MAX_CHANNEL_UTILIZATION 423 && utilizationRatio >= MIN_CHANNEL_UTILIZATION); 424 } 425 426 // Calculate the available airtime fraction value which is multiplied by 427 // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as 428 // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION calculateAirTimeFraction(int channelUtilization, int channelWidthFactor)429 private int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) { 430 int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization; 431 int airTimeFraction = airTimeFraction20MHz; 432 // For the cases of 40MHz or above, need to take 433 // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor) 434 // because channelUtilization is defined for primary 20MHz channel 435 for (int i = 1; i <= channelWidthFactor; ++i) { 436 airTimeFraction *= airTimeFraction; 437 airTimeFraction /= MAX_CHANNEL_UTILIZATION; 438 } 439 if (mVerboseLoggingEnabled) { 440 Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction); 441 } 442 return airTimeFraction; 443 } 444 } 445