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                 INVALID, 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                 INVALID, 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                 channelUtilizationBssLoad,
281                 channelUtilizationLinkLayerStats,
282                 isBluetoothConnected);
283 
284         return predictThroughputInternal(wifiStandard, false/* is11bMode */, channelWidth,
285                 rssiDbm, maxNumSpatialStream, channelUtilization, frequency,
286                 disabledSubchannelBitmap);
287     }
288 
predictThroughputInternal(@ifiStandard int wifiStandard, boolean is11bMode, int channelWidth, int rssiDbm, int maxNumSpatialStream, int channelUtilization, int frequency, @Nullable byte[] disabledSubchannelBitmap)289     private int predictThroughputInternal(@WifiStandard int wifiStandard, boolean is11bMode,
290             int channelWidth, int rssiDbm, int maxNumSpatialStream,  int channelUtilization,
291             int frequency, @Nullable byte[] disabledSubchannelBitmap) {
292 
293         // channel bandwidth in MHz = 20MHz * (2 ^ channelWidthFactor);
294         int channelWidthFactor;
295         int numTonePerSym;
296         int symDurationNs;
297         int maxBitsPerTone;
298         if (maxNumSpatialStream < 1) {
299             Log.e(TAG, "maxNumSpatialStream < 1 due to wrong implementation. Overridden to 1");
300             maxNumSpatialStream = 1;
301         }
302         if (wifiStandard == ScanResult.WIFI_STANDARD_LEGACY) {
303             // For simplicity, use legacy OFDM parameters to predict 11b rate
304             numTonePerSym = NUM_TONE_PER_SYM_LEGACY;
305             channelWidthFactor = 0;
306             maxNumSpatialStream = MAX_NUM_SPATIAL_STREAM_LEGACY;
307             maxBitsPerTone = MAX_BITS_PER_TONE_LEGACY;
308             symDurationNs = SYM_DURATION_LEGACY_NS;
309         } else if (wifiStandard == ScanResult.WIFI_STANDARD_11N) {
310             if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) {
311                 numTonePerSym = NUM_TONE_PER_SYM_11N_20MHZ;
312                 channelWidthFactor = 0;
313             } else {
314                 numTonePerSym = NUM_TONE_PER_SYM_11N_40MHZ;
315                 channelWidthFactor = 1;
316             }
317             maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11N);
318             maxBitsPerTone = MAX_BITS_PER_TONE_11N;
319             symDurationNs = SYM_DURATION_11N_NS;
320         } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AC) {
321             if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) {
322                 numTonePerSym = NUM_TONE_PER_SYM_11AC_20MHZ;
323                 channelWidthFactor = 0;
324             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) {
325                 numTonePerSym = NUM_TONE_PER_SYM_11AC_40MHZ;
326                 channelWidthFactor = 1;
327             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) {
328                 numTonePerSym = NUM_TONE_PER_SYM_11AC_80MHZ;
329                 channelWidthFactor = 2;
330             } else {
331                 numTonePerSym = NUM_TONE_PER_SYM_11AC_160MHZ;
332                 channelWidthFactor = 3;
333             }
334             maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AC);
335             maxBitsPerTone = MAX_BITS_PER_TONE_11AC;
336             symDurationNs = SYM_DURATION_11AC_NS;
337         } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AX) {
338             if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) {
339                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_20MHZ;
340                 channelWidthFactor = 0;
341             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) {
342                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_40MHZ;
343                 channelWidthFactor = 1;
344             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) {
345                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_80MHZ;
346                 channelWidthFactor = 2;
347             } else {
348                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_160MHZ;
349                 channelWidthFactor = 3;
350             }
351             maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AX);
352             maxBitsPerTone = MAX_BITS_PER_TONE_11AX;
353             symDurationNs = SYM_DURATION_11AX_BE_NS;
354         } else if (wifiStandard == ScanResult.WIFI_STANDARD_11BE) {
355             if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) {
356                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_20MHZ;
357                 channelWidthFactor = 0;
358             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) {
359                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_40MHZ;
360                 channelWidthFactor = 1;
361             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) {
362                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_80MHZ;
363                 channelWidthFactor = 2;
364             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_160MHZ) {
365                 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_160MHZ;
366                 channelWidthFactor = 3;
367             } else {
368                 numTonePerSym = NUM_TONE_PER_SYM_11BE_320MHZ;
369                 channelWidthFactor = 4;
370             }
371             int numPunctured20MhzSubChannel = 0;
372             if (disabledSubchannelBitmap != null && disabledSubchannelBitmap.length == 2) {
373                 numPunctured20MhzSubChannel = Integer.bitCount(
374                         (disabledSubchannelBitmap[1] << 8) | disabledSubchannelBitmap[0]);
375             }
376             if (numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ < numTonePerSym) {
377                 numTonePerSym -= numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ;
378             }
379             maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11BE);
380             maxBitsPerTone = MAX_BITS_PER_TONE_11BE;
381             symDurationNs = SYM_DURATION_11AX_BE_NS;
382         } else {
383             return WifiInfo.LINK_SPEED_UNKNOWN;
384         }
385 
386         // 6Ghz RSSI boost
387         if (mContext.getResources().getBoolean(R.bool.config_wifiEnable6GhzBeaconRssiBoost)
388                 && ScanResult.is6GHz(frequency)) {
389             switch (channelWidth) {
390                 case ScanResult.CHANNEL_WIDTH_40MHZ:
391                     rssiDbm += 3;
392                     break;
393                 case ScanResult.CHANNEL_WIDTH_80MHZ:
394                     rssiDbm += 6;
395                     break;
396                 case ScanResult.CHANNEL_WIDTH_160MHZ:
397                     rssiDbm += 9;
398                     break;
399                 case ScanResult.CHANNEL_WIDTH_320MHZ:
400                     rssiDbm += 12;
401                     break;
402                 default:
403                     // do nothing
404             }
405         }
406 
407         // noiseFloorDbBoost = 10 * log10 * (2 ^ channelWidthFactor)
408         int noiseFloorDbBoost = TWO_IN_DB * channelWidthFactor;
409         int noiseFloorDbm = NOISE_FLOOR_20MHZ_DBM + noiseFloorDbBoost + SNR_MARGIN_DB;
410         int snrDb  = rssiDbm - noiseFloorDbm;
411 
412         int bitPerTone = calculateBitPerTone(snrDb);
413         bitPerTone = Math.min(bitPerTone, maxBitsPerTone);
414 
415         long bitPerToneTotal = bitPerTone * maxNumSpatialStream;
416         long numBitPerSym = bitPerToneTotal * numTonePerSym;
417         int phyRateMbps =  (int) ((numBitPerSym * MICRO_TO_NANO_RATIO)
418                 / (symDurationNs * BIT_PER_TONE_SCALE));
419 
420         int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor);
421 
422         int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION;
423 
424         if (is11bMode) {
425             throughputMbps = Math.min(throughputMbps, B_MODE_MAX_MBPS);
426         }
427         if (mVerboseLoggingEnabled) {
428             StringBuilder sb = new StringBuilder();
429             Log.d(TAG, sb.append(" BW: ").append(channelWidth)
430                     .append(" RSSI: ").append(rssiDbm)
431                     .append(" Nss: ").append(maxNumSpatialStream)
432                     .append(" Mode: ").append(wifiStandard)
433                     .append(" symDur: ").append(symDurationNs)
434                     .append(" snrDb ").append(snrDb)
435                     .append(" bitPerTone: ").append(bitPerTone)
436                     .append(" rate: ").append(phyRateMbps)
437                     .append(" throughput: ").append(throughputMbps)
438                     .toString());
439         }
440         return throughputMbps;
441     }
442 
443     // Calculate the number of bits per tone based on the input of SNR in dB
444     // The output is scaled up by BIT_PER_TONE_SCALE for integer representation
calculateBitPerTone(int snrDb)445     private static int calculateBitPerTone(int snrDb) {
446         int bitPerTone;
447         if (snrDb <= SNR_DB_TO_BIT_PER_TONE_LUT_MAX) {
448             int lut_in_idx = Math.max(snrDb, SNR_DB_TO_BIT_PER_TONE_LUT_MIN)
449                     - SNR_DB_TO_BIT_PER_TONE_LUT_MIN;
450             lut_in_idx = Math.min(lut_in_idx, SNR_DB_TO_BIT_PER_TONE_LUT.length - 1);
451             bitPerTone = SNR_DB_TO_BIT_PER_TONE_LUT[lut_in_idx];
452         } else {
453             // bitPerTone = Math.log10(1+snr)/Math.log10(2) can be approximated as
454             // Math.log10(snr) / 0.3 = log10(10^(snrDb/10)) / 0.3 = snrDb / 3
455             // SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / 3
456             bitPerTone = snrDb * SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE;
457         }
458         return bitPerTone;
459     }
460 
getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected)461     private int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad,
462             int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) {
463         int channelUtilization;
464         boolean is2G = ScanResult.is24GHz(frequency);
465         if (isValidUtilizationRatio(channelUtilizationBssLoad)) {
466             channelUtilization = channelUtilizationBssLoad;
467         } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) {
468             channelUtilization = channelUtilizationLinkLayerStats;
469         } else {
470             channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G :
471                     CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G;
472         }
473 
474         if (is2G && isBluetoothConnected) {
475             channelUtilization += CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G;
476             channelUtilization = Math.min(channelUtilization, MAX_CHANNEL_UTILIZATION);
477         }
478         if (mVerboseLoggingEnabled) {
479             StringBuilder sb = new StringBuilder();
480             Log.d(TAG, sb.append(" utilization (BssLoad) ").append(channelUtilizationBssLoad)
481                     .append(" utilization (LLStats) ").append(channelUtilizationLinkLayerStats)
482                     .append(" isBluetoothConnected: ").append(isBluetoothConnected)
483                     .append(" final utilization: ").append(channelUtilization)
484                     .toString());
485         }
486         return channelUtilization;
487     }
488 
489     /**
490      * Check if the channel utilization ratio is valid
491      */
isValidUtilizationRatio(int utilizationRatio)492     private static boolean isValidUtilizationRatio(int utilizationRatio) {
493         return (utilizationRatio <= MAX_CHANNEL_UTILIZATION
494                 && utilizationRatio >= MIN_CHANNEL_UTILIZATION);
495     }
496 
497     // Calculate the available airtime fraction value which is multiplied by
498     // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as
499     // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION
calculateAirTimeFraction(int channelUtilization, int channelWidthFactor)500     private int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) {
501         int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization;
502         int airTimeFraction = airTimeFraction20MHz;
503         // For the cases of 40MHz or above, need to take
504         // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor)
505         // because channelUtilization is defined for primary 20MHz channel
506         for (int i = 1; i <= channelWidthFactor; ++i) {
507             airTimeFraction *= airTimeFraction;
508             airTimeFraction /= MAX_CHANNEL_UTILIZATION;
509         }
510         if (mVerboseLoggingEnabled) {
511             Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction);
512         }
513         return airTimeFraction;
514     }
515 }
516