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