• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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