diff options
author | Kai Shi <kaishi@google.com> | 2019-11-16 04:46:33 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-11-16 04:46:33 +0000 |
commit | 7703348beff18ed6cfe4abec9e4d8ab227833c85 (patch) | |
tree | 276fd402836ca2191499d33e37b4475f5f6ddcfd /service | |
parent | 131b8497a10907d6db3678ab0df9c42a32c5cf8b (diff) | |
parent | 6ff1d98e75558a16574fbbbb6fe234d118678c9f (diff) |
Merge "Wifi: add throughputPredictor and throughputScorer classes"
Diffstat (limited to 'service')
9 files changed, 528 insertions, 7 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 68dfe083e..868865134 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -3103,6 +3103,7 @@ public class ClientModeImpl extends StateMachine { case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: mBluetoothConnectionActive = (message.arg1 != BluetoothAdapter.STATE_DISCONNECTED); + mWifiConnectivityManager.setBluetoothConnected(mBluetoothConnectionActive); break; case CMD_ENABLE_RSSI_POLL: mEnableRssiPolling = (message.arg1 == 1); @@ -3947,6 +3948,7 @@ public class ClientModeImpl extends StateMachine { != BluetoothAdapter.STATE_DISCONNECTED); mWifiNative.setBluetoothCoexistenceScanMode( mInterfaceName, mBluetoothConnectionActive); + mWifiConnectivityManager.setBluetoothConnected(mBluetoothConnectionActive); break; case CMD_SET_SUSPEND_OPT_ENABLED: if (message.arg1 == 1) { diff --git a/service/java/com/android/server/wifi/ThroughputPredictor.java b/service/java/com/android/server/wifi/ThroughputPredictor.java new file mode 100644 index 000000000..a8e8adac3 --- /dev/null +++ b/service/java/com/android/server/wifi/ThroughputPredictor.java @@ -0,0 +1,296 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wifi; + +import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MAX_CHANNEL_UTILIZATION; +import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MIN_CHANNEL_UTILIZATION; + +import android.content.Context; +import android.net.wifi.ScanResult; +import android.util.Log; + +import com.android.wifi.R; + +/** + * A class that predicts network throughput based on RSSI, channel utilization, channel width, + * WiFi standard (PHY/MAC mode), Nss and other radio information. + */ +public class ThroughputPredictor { + private static final String TAG = "WifiThroughputPredictor"; + private static final boolean DBG = false; + + // Default value of channel utilization at 2G when channel utilization is not available from + // BssLoad IE or from link layer stats + public static final int CHANNEL_UTILIZATION_DEFAULT_2G = MAX_CHANNEL_UTILIZATION * 6 / 16; + // Default value of channel utilization at 5G when channel utilization is not available from + // BssLoad IE or from link layer stats + public static final int CHANNEL_UTILIZATION_DEFAULT_5G = MAX_CHANNEL_UTILIZATION / 16; + // Channel utilization boost when bluetooth is in the connected mode + public static final int CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G = MAX_CHANNEL_UTILIZATION / 4; + + // Number of data tones per OFDM symbol + private static final int NUM_TONE_PER_SYM_LEGACY = 48; + private static final int NUM_TONE_PER_SYM_11N_20MHZ = 52; + private static final int NUM_TONE_PER_SYM_11N_40MHZ = 108; + private static final int NUM_TONE_PER_SYM_11AC_20MHZ = 52; + private static final int NUM_TONE_PER_SYM_11AC_40MHZ = 108; + private static final int NUM_TONE_PER_SYM_11AC_80MHZ = 234; + private static final int NUM_TONE_PER_SYM_11AC_160MHZ = 468; + private static final int NUM_TONE_PER_SYM_11AX_20MHZ = 234; + private static final int NUM_TONE_PER_SYM_11AX_40MHZ = 468; + private static final int NUM_TONE_PER_SYM_11AX_80MHZ = 980; + private static final int NUM_TONE_PER_SYM_11AX_160MHZ = 1960; + + // 11ag OFDM symbol duration in ns + private static final int SYM_DURATION_LEGACY_NS = 4000; + // 11n OFDM symbol duration in ns with 0.4us guard interval + private static final int SYM_DURATION_11N_NS = 3600; + // 11ac OFDM symbol duration in ns with 0.4us guard interval + private static final int SYM_DURATION_11AC_NS = 3600; + // 11n OFDM symbol duration in ns with 0.8us guard interval + private static final int SYM_DURATION_11AX_NS = 13600; + private static final int MICRO_TO_NANO_RATIO = 1000; + + // The scaling factor for integer representation of bitPerTone and MAX_BITS_PER_TONE_XXX + private static final int BIT_PER_TONE_SCALE = 1000; + private static final int MAX_BITS_PER_TONE_LEGACY = (int) (6 * 3.0 * BIT_PER_TONE_SCALE / 4.0); + private static final int MAX_BITS_PER_TONE_11N = (int) (6 * 5.0 * BIT_PER_TONE_SCALE / 6.0); + private static final int MAX_BITS_PER_TONE_11AC = (int) (8 * 5.0 * BIT_PER_TONE_SCALE / 6.0); + private static final int MAX_BITS_PER_TONE_11AX = (int) (10 * 5.0 * BIT_PER_TONE_SCALE / 6.0); + + // snrDb-to-bitPerTone lookup table (LUT) used at low SNR + // snr = Math.pow(10.0, snrDb / 10.0); + // bitPerTone = (int) (Math.log10(1 + snr) / Math.log10(2.0) * BIT_PER_TONE_SCALE) + private static final int TWO_IN_DB = 3; + private static final int SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / TWO_IN_DB; + private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MIN = -10; // minimum snrDb supported by LUT + private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MAX = 9; // maximum snrDb supported by LUT + private static final int[] SNR_DB_TO_BIT_PER_TONE_LUT = {0, 171, 212, 262, 323, 396, 484, 586, + 706, 844, 1000, 1176, 1370, 1583, 1812, 2058, 2317, 2588, 2870, 3161}; + // Thermal noise floor power in dBm integrated over 20MHz with 5.5dB noise figure at 25C + private static final int NOISE_FLOOR_20MHZ_DBM = -96; + // A fudge factor to represent HW implementation margin in dB. + // Predicted throughput matches pretty well with OTA throughput with this fudge factor. + private static final int SNR_MARGIN_DB = 16; + private static final int MAX_NUM_SPATIAL_STREAM_11AX = 8; + private static final int MAX_NUM_SPATIAL_STREAM_11AC = 8; + private static final int MAX_NUM_SPATIAL_STREAM_11N = 4; + private static final int MAX_NUM_SPATIAL_STREAM_LEGACY = 1; + + private final boolean mAxSupported; + private final boolean mContiguous160MHzSupported; + private final int mMaxNumSpatialStreamSupported; + + ThroughputPredictor(Context context) { + // TODO: b/144576344 get the following information from HAL + mAxSupported = context.getResources().getBoolean( + R.bool.config_wifi_11ax_supported); + mContiguous160MHzSupported = context.getResources().getBoolean( + R.bool.config_wifi_contiguous_160mhz_supported); + mMaxNumSpatialStreamSupported = context.getResources().getInteger( + R.integer.config_wifi_max_num_spatial_stream_supported); + } + + /** + * Predict network throughput + * @param wifiStandard the highest wifi standard supported by AP + * @param channelWidthAp the channel bandwidth of AP + * @param rssiDbm the scan RSSI in dBm + * @param frequency the center frequency of primary 20MHz channel + * @param maxNumSpatialStreamAp the maximum number of spatial streams supported by AP + * @param channelUtilizationBssLoad the channel utilization ratio indicated from BssLoad IE + * @param channelUtilizationLinkLayerStats the channel utilization ratio detected from scan + * @param isBluetoothConnected whether the bluetooth adaptor is in connected mode + * @return predicted throughput in Mbps + */ + public int predictThroughput(@ScanResult.WifiStandard int wifiStandard, + int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp, + int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, + boolean isBluetoothConnected) { + + int maxNumSpatialStream = Math.min(mMaxNumSpatialStreamSupported, maxNumSpatialStreamAp); + + // Downgrade to AC mode if 11AX AP is found but 11AX mode is not supported by the device + if (!mAxSupported && wifiStandard == ScanResult.WIFI_STANDARD_11AX) { + wifiStandard = ScanResult.WIFI_STANDARD_11AC; + } + + int channelWidth = channelWidthAp; + // Downgrade to 80MHz if 160MHz AP is found but 160MHz mode is not supported by the device + if (!mContiguous160MHzSupported && (channelWidth == ScanResult.CHANNEL_WIDTH_160MHZ)) { + channelWidth = ScanResult.CHANNEL_WIDTH_80MHZ; + } + + // channel bandwidth in MHz = 20MHz * (2 ^ channelWidthFactor); + int channelWidthFactor; + int numTonePerSym; + int symDurationNs; + int maxBitsPerTone; + if (wifiStandard == ScanResult.WIFI_STANDARD_LEGACY) { + numTonePerSym = NUM_TONE_PER_SYM_LEGACY; + channelWidthFactor = 0; + maxNumSpatialStream = MAX_NUM_SPATIAL_STREAM_LEGACY; + maxBitsPerTone = MAX_BITS_PER_TONE_LEGACY; + symDurationNs = SYM_DURATION_LEGACY_NS; + } else if (wifiStandard == ScanResult.WIFI_STANDARD_11N) { + if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11N_20MHZ; + channelWidthFactor = 0; + } else { + numTonePerSym = NUM_TONE_PER_SYM_11N_40MHZ; + channelWidthFactor = 1; + } + maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11N); + maxBitsPerTone = MAX_BITS_PER_TONE_11N; + symDurationNs = SYM_DURATION_11N_NS; + } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AC) { + if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AC_20MHZ; + channelWidthFactor = 0; + } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AC_40MHZ; + channelWidthFactor = 1; + } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AC_80MHZ; + channelWidthFactor = 2; + } else { + numTonePerSym = NUM_TONE_PER_SYM_11AC_160MHZ; + channelWidthFactor = 3; + } + maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AC); + maxBitsPerTone = MAX_BITS_PER_TONE_11AC; + symDurationNs = SYM_DURATION_11AC_NS; + } else { // ScanResult.WIFI_STANDARD_11AX + if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AX_20MHZ; + channelWidthFactor = 0; + } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AX_40MHZ; + channelWidthFactor = 1; + } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { + numTonePerSym = NUM_TONE_PER_SYM_11AX_80MHZ; + channelWidthFactor = 2; + } else { + numTonePerSym = NUM_TONE_PER_SYM_11AX_160MHZ; + channelWidthFactor = 3; + } + maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AX); + maxBitsPerTone = MAX_BITS_PER_TONE_11AX; + symDurationNs = SYM_DURATION_11AX_NS; + } + // noiseFloorDbBoost = 10 * log10 * (2 ^ channelWidthFactor) + int noiseFloorDbBoost = TWO_IN_DB * channelWidthFactor; + int noiseFloorDbm = NOISE_FLOOR_20MHZ_DBM + noiseFloorDbBoost + SNR_MARGIN_DB; + int snrDb = rssiDbm - noiseFloorDbm; + + int bitPerTone = calculateBitPerTone(snrDb); + bitPerTone = Math.min(bitPerTone, maxBitsPerTone); + + long bitPerToneTotal = bitPerTone * maxNumSpatialStream; + long numBitPerSym = bitPerToneTotal * numTonePerSym; + int phyRateMbps = (int) ((numBitPerSym * MICRO_TO_NANO_RATIO) + / (symDurationNs * BIT_PER_TONE_SCALE)); + + int channelUtilization = getValidChannelUtilization(frequency, + channelUtilizationBssLoad, + channelUtilizationLinkLayerStats, + isBluetoothConnected); + + int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor); + + int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION; + + if (DBG) { + Log.d(TAG, " BW: " + channelWidthAp + " RSSI: " + + rssiDbm + " Nss: " + maxNumSpatialStreamAp + " freq: " + frequency + + " Mode: " + wifiStandard + " symDur: " + symDurationNs + + " snrDb " + snrDb + " bitPerTone: " + bitPerTone + + " rate: " + phyRateMbps + " throughput: " + throughputMbps); + } + return throughputMbps; + } + + // Calculate the number of bits per tone based on the input of SNR in dB + // The output is scaled up by BIT_PER_TONE_SCALE for integer representation + private static int calculateBitPerTone(int snrDb) { + int bitPerTone; + if (snrDb <= SNR_DB_TO_BIT_PER_TONE_LUT_MAX) { + int lut_in_idx = Math.max(snrDb, SNR_DB_TO_BIT_PER_TONE_LUT_MIN) + - SNR_DB_TO_BIT_PER_TONE_LUT_MIN; + lut_in_idx = Math.min(lut_in_idx, SNR_DB_TO_BIT_PER_TONE_LUT.length - 1); + bitPerTone = SNR_DB_TO_BIT_PER_TONE_LUT[lut_in_idx]; + } else { + // bitPerTone = Math.log10(1+snr)/Math.log10(2) can be approximated as + // Math.log10(snr) / 0.3 = log10(10^(snrDb/10)) / 0.3 = snrDb / 3 + // SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / 3 + bitPerTone = snrDb * SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE; + } + return bitPerTone; + } + + private static int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, + int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) { + int channelUtilization; + boolean is2G = (frequency < ScoringParams.MINIMUM_5GHZ_BAND_FREQUENCY_IN_MEGAHERTZ); + if (isValidUtilizationRatio(channelUtilizationBssLoad)) { + channelUtilization = channelUtilizationBssLoad; + } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) { + channelUtilization = channelUtilizationLinkLayerStats; + } else { + channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G : + CHANNEL_UTILIZATION_DEFAULT_5G; + } + + if (is2G && isBluetoothConnected) { + channelUtilization += CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G; + channelUtilization = Math.min(channelUtilization, MAX_CHANNEL_UTILIZATION); + } + if (DBG) { + Log.d(TAG, " utilization (BssLoad) " + channelUtilizationBssLoad + + " utilization (LLStats) " + channelUtilizationLinkLayerStats + + " isBluetoothConnected: " + isBluetoothConnected + + " final utilization: " + channelUtilization); + } + return channelUtilization; + } + + /** + * Check if the channel utilization ratio is valid + */ + private static boolean isValidUtilizationRatio(int utilizationRatio) { + return (utilizationRatio <= MAX_CHANNEL_UTILIZATION + && utilizationRatio >= MIN_CHANNEL_UTILIZATION); + } + + // Calculate the available airtime fraction value which is multiplied by + // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as + // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION + private static int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) { + int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization; + int airTimeFraction = airTimeFraction20MHz; + // For the cases of 40MHz or above, need to take + // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor) + // because channelUtilization is defined for primary 20MHz channel + for (int i = 1; i <= channelWidthFactor; ++i) { + airTimeFraction *= airTimeFraction; + airTimeFraction /= MAX_CHANNEL_UTILIZATION; + } + if (DBG) { + Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction); + } + return airTimeFraction; + } +} diff --git a/service/java/com/android/server/wifi/ThroughputScorer.java b/service/java/com/android/server/wifi/ThroughputScorer.java new file mode 100644 index 000000000..1e42657c6 --- /dev/null +++ b/service/java/com/android/server/wifi/ThroughputScorer.java @@ -0,0 +1,143 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.NonNull; +import android.util.Log; + +import com.android.server.wifi.WifiCandidates.Candidate; +import com.android.server.wifi.WifiCandidates.ScoredCandidate; + +import java.util.Collection; + +/** + * A candidate scorer that combines RSSI base score and network throughput score. + */ +final class ThroughputScorer implements WifiCandidates.CandidateScorer { + private static final String TAG = "ThroughputScorer"; + private static final boolean DBG = false; + /** + * This should match WifiNetworkSelector.experimentIdFromIdentifier(getIdentifier()) + * when using the default ScoringParams. + */ + public static final int THROUGHPUT_SCORER_DEFAULT_EXPID = 42330058; + + private final ScoringParams mScoringParams; + + // config_wifi_framework_RSSI_SCORE_OFFSET + public static final int RSSI_SCORE_OFFSET = 85; + + // config_wifi_framework_RSSI_SCORE_SLOPE + public static final int RSSI_SCORE_SLOPE_IS_4 = 4; + + // config_wifi_framework_SECURITY_AWARD + public static final int SECURITY_AWARD = 10; + + // config_wifi_framework_LAST_SELECTION_AWARD + public static final int LAST_SELECTION_AWARD_IS_480 = 480; + + // Bonus score for current network + // High RSSI case: + // Bonus RSSI score: 10 (equivalent to RSSI variation 2.5dB) + // Bonus throughput score: 10 (equivalent to ~ 40Mbps). + // Low RSSI case: + // Bonus RSSI score: 16 (equivalent to RSSI variation 4dB) + // Bonus throughput score: 4 (equivalent to ~ 16Mbps). + public static final int CURRENT_NETWORK_BOOST = 20; + + // Max throughput in 11AC 40MHz 2SS mode with zero channel utilization + public static final int MAX_THROUGHPUT_AC_40_MHZ_2SS_MBPS = 433; + // Max throughput score in 11AC 40MHz 2SS mode + public static final int MAX_THROUGHPUT_BONUS_SCORE_AC_40_MHZ_2SS = 120; + + // Max throughput bonus score for all possible modes + public static final int MAX_THROUGHPUT_BONUS_SCORE = 200; + + private static final boolean USE_USER_CONNECT_CHOICE = true; + + ThroughputScorer(ScoringParams scoringParams) { + mScoringParams = scoringParams; + } + + @Override + public String getIdentifier() { + return "ThroughputScorer"; + } + + /** + * Calculates an individual candidate's score. + */ + private ScoredCandidate scoreCandidate(Candidate candidate) { + int rssiSaturationThreshold = mScoringParams.getGoodRssi(candidate.getFrequency()); + int rssi = Math.min(candidate.getScanRssi(), rssiSaturationThreshold); + int rssiBaseScore = (rssi + RSSI_SCORE_OFFSET) * RSSI_SCORE_SLOPE_IS_4; + + int throughputBonusScore = calculateThroughputBonusScore(candidate); + + int lastSelectionBonusScore = (int) + (candidate.getLastSelectionWeight() * LAST_SELECTION_AWARD_IS_480); + + int currentNetworkBoost = candidate.isCurrentNetwork() ? CURRENT_NETWORK_BOOST : 0; + + int securityAward = candidate.isOpenNetwork() ? 0 : SECURITY_AWARD; + + // To simulate the old strict priority rule, subtract a penalty based on + // which evaluator added the candidate. + int evaluatorGroupScore = -1000 * candidate.getEvaluatorId(); + + int score = rssiBaseScore + throughputBonusScore + lastSelectionBonusScore + + currentNetworkBoost + securityAward + evaluatorGroupScore; + + if (DBG) { + Log.d(TAG, " rssiScore: " + rssiBaseScore + + " throughputScore: " + throughputBonusScore + + " lastSelectionBonus: " + lastSelectionBonusScore + + " currentNetworkBoost: " + currentNetworkBoost + + " securityAward: " + securityAward + + " evaluatorScore: " + evaluatorGroupScore + + " final score: " + score); + } + + // The old method breaks ties on the basis of RSSI, which we can + // emulate easily since our score does not need to be an integer. + double tieBreaker = candidate.getScanRssi() / 1000.0; + return new ScoredCandidate(score + tieBreaker, 10, + USE_USER_CONNECT_CHOICE, candidate); + } + + private int calculateThroughputBonusScore(Candidate candidate) { + int throughputScoreRaw = candidate.getPredictedThroughputMbps() + * MAX_THROUGHPUT_BONUS_SCORE_AC_40_MHZ_2SS + / MAX_THROUGHPUT_AC_40_MHZ_2SS_MBPS; + return Math.min(throughputScoreRaw, MAX_THROUGHPUT_BONUS_SCORE); + } + + @Override + public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates) { + ScoredCandidate choice = ScoredCandidate.NONE; + for (Candidate candidate : candidates) { + ScoredCandidate scoredCandidate = scoreCandidate(candidate); + if (scoredCandidate.value > choice.value) { + choice = scoredCandidate; + } + } + // Here we just return the highest scored candidate; we could + // compute a new score, if desired. + return choice; + } + +} diff --git a/service/java/com/android/server/wifi/WifiCandidates.java b/service/java/com/android/server/wifi/WifiCandidates.java index 00e67f44d..d0f354232 100644 --- a/service/java/com/android/server/wifi/WifiCandidates.java +++ b/service/java/com/android/server/wifi/WifiCandidates.java @@ -117,6 +117,10 @@ public class WifiCandidates { */ int getFrequency(); /** + * Gets the predicted throughput in Mbps + */ + int getPredictedThroughputMbps(); + /** * Gets statistics from the scorecard. */ @Nullable WifiScoreCardProto.Signal getEventStatistics(WifiScoreCardProto.Event event); @@ -138,6 +142,7 @@ public class WifiCandidates { private final boolean mIsCurrentNetwork; private final boolean mIsCurrentBssid; private final boolean mIsMetered; + private final int mPredictedThroughputMbps; CandidateImpl(Key key, ScanDetail scanDetail, @@ -148,7 +153,8 @@ public class WifiCandidates { double lastSelectionWeight, boolean isCurrentNetwork, boolean isCurrentBssid, - boolean isMetered) { + boolean isMetered, + int predictedThroughputMbps) { this.key = key; this.scanDetail = scanDetail; this.config = config; @@ -159,6 +165,7 @@ public class WifiCandidates { this.mIsCurrentNetwork = isCurrentNetwork; this.mIsCurrentBssid = isCurrentBssid; this.mIsMetered = isMetered; + this.mPredictedThroughputMbps = predictedThroughputMbps; } @Override @@ -237,6 +244,11 @@ public class WifiCandidates { return scanDetail.getScanResult().frequency; } + @Override + public int getPredictedThroughputMbps() { + return mPredictedThroughputMbps; + } + /** * Accesses statistical information from the score card */ @@ -359,7 +371,8 @@ public class WifiCandidates { @WifiNetworkSelector.NetworkEvaluator.EvaluatorId int evaluatorId, int evaluatorScore, double lastSelectionWeightBetweenZeroAndOne, - boolean isMetered) { + boolean isMetered, + int predictedThroughputMbps) { if (config == null) return failure(); if (scanDetail == null) return failure(); ScanResult scanResult = scanDetail.getScanResult(); @@ -393,7 +406,8 @@ public class WifiCandidates { Math.min(Math.max(lastSelectionWeightBetweenZeroAndOne, 0.0), 1.0), config.networkId == mCurrentNetworkId, bssid.equals(mCurrentBssid), - isMetered); + isMetered, + predictedThroughputMbps); mCandidates.put(key, candidate); return true; } diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 41301385c..f74ec3f99 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -274,6 +274,13 @@ public class WifiConnectivityManager { } } + /** + * Set whether bluetooth is in the connected state + */ + public void setBluetoothConnected(boolean isBlueToothConnected) { + mNetworkSelector.setBluetoothConnected(isBlueToothConnected); + } + // All single scan results listener. // // Note: This is the listener for all the available single scan results, @@ -613,6 +620,7 @@ public class WifiConnectivityManager { mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()); mBssidBlocklistMonitor = mWifiInjector.getBssidBlocklistMonitor(); mWifiChannelUtilization = mWifiInjector.getWifiChannelUtilization(); + mNetworkSelector.setWifiChannelUtilization(mWifiChannelUtilization); } /** Returns maximum PNO score, before any awards/bonuses. */ diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 0034cc243..a53c4ec32 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -159,6 +159,7 @@ public class WifiInjector { private final TelephonyUtil mTelephonyUtil; private WifiChannelUtilization mWifiChannelUtilization; private final KeyStore mKeyStore; + private final ThroughputPredictor mThroughputPredictor; public WifiInjector(Context context) { if (context == null) { @@ -271,14 +272,18 @@ public class WifiInjector { mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? 256 : 512); mScoringParams = new ScoringParams(mContext, mFrameworkFacade, wifiHandler); mWifiMetrics.setScoringParams(mScoringParams); + mThroughputPredictor = new ThroughputPredictor(mContext); mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiScoreCard, mScoringParams, - mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics, mWifiNative); + mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics, mWifiNative, + mThroughputPredictor); CompatibilityScorer compatibilityScorer = new CompatibilityScorer(mScoringParams); mWifiNetworkSelector.registerCandidateScorer(compatibilityScorer); ScoreCardBasedScorer scoreCardBasedScorer = new ScoreCardBasedScorer(mScoringParams); mWifiNetworkSelector.registerCandidateScorer(scoreCardBasedScorer); BubbleFunScorer bubbleFunScorer = new BubbleFunScorer(mScoringParams); mWifiNetworkSelector.registerCandidateScorer(bubbleFunScorer); + ThroughputScorer throughputScorer = new ThroughputScorer(mScoringParams); + mWifiNetworkSelector.registerCandidateScorer(throughputScorer); mWifiMetrics.setWifiNetworkSelector(mWifiNetworkSelector); mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext, mScoringParams, mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiConnectivityHelper, diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index 5d4a12eda..f111a4cfc 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -37,6 +37,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.wifi.proto.nano.WifiMetricsProto; +import com.android.server.wifi.util.InformationElementUtil.BssLoad; import com.android.server.wifi.util.ScanResultUtil; import com.android.wifi.R; @@ -119,6 +120,9 @@ public class WifiNetworkSelector { private final Map<String, WifiCandidates.CandidateScorer> mCandidateScorers = new ArrayMap<>(); private boolean mIsEnhancedOpenSupportedInitialized = false; private boolean mIsEnhancedOpenSupported; + private ThroughputPredictor mThroughputPredictor; + private boolean mIsBluetoothConnected = false; + private WifiChannelUtilization mWifiChannelUtilization; /** * WiFi Network Selector supports various categories of networks. Each category @@ -726,7 +730,8 @@ public class WifiNetworkSelector { score, (config.networkId == lastUserSelectedNetworkId) ? lastSelectionWeight : 0.0, - WifiConfiguration.isMetered(config, wifiInfo)); + WifiConfiguration.isMetered(config, wifiInfo), + predictThroughput(scanDetail)); mWifiMetrics.setNominatorForNetwork(config.networkId, evaluatorIdToNominatorId(registeredEvaluator.getId())); } @@ -883,6 +888,27 @@ public class WifiNetworkSelector { return ans; } + private int predictThroughput(@NonNull ScanDetail scanDetail) { + if (scanDetail.getScanResult() == null || scanDetail.getNetworkDetail() == null) { + return 0; + } + int channelUtilizationLinkLayerStats = BssLoad.INVALID; + if (mWifiChannelUtilization != null) { + channelUtilizationLinkLayerStats = + mWifiChannelUtilization.getUtilizationRatio( + scanDetail.getScanResult().frequency); + } + return mThroughputPredictor.predictThroughput( + scanDetail.getScanResult().getWifiStandard(), + scanDetail.getScanResult().channelWidth, + scanDetail.getScanResult().level, + scanDetail.getScanResult().frequency, + scanDetail.getNetworkDetail().getMaxNumberSpatialStreams(), + scanDetail.getNetworkDetail().getChannelUtilization(), + channelUtilizationLinkLayerStats, + mIsBluetoothConnected); + } + /** * Register a network evaluator * @@ -928,9 +954,24 @@ public class WifiNetworkSelector { private static final int ID_PREFIX = 42; private static final int MIN_SCORER_EXP_ID = ID_PREFIX * ID_SUFFIX_MOD; + /** + * Set Wifi channel utilization calculated from link layer stats + */ + public void setWifiChannelUtilization(WifiChannelUtilization wifiChannelUtilization) { + mWifiChannelUtilization = wifiChannelUtilization; + } + + /** + * Set whether bluetooth is in the connected state + */ + public void setBluetoothConnected(boolean isBlueToothConnected) { + mIsBluetoothConnected = isBlueToothConnected; + } + WifiNetworkSelector(Context context, WifiScoreCard wifiScoreCard, ScoringParams scoringParams, WifiConfigManager configManager, Clock clock, LocalLog localLog, - WifiMetrics wifiMetrics, WifiNative wifiNative) { + WifiMetrics wifiMetrics, WifiNative wifiNative, + ThroughputPredictor throughputPredictor) { mWifiConfigManager = configManager; mClock = clock; mWifiScoreCard = wifiScoreCard; @@ -938,7 +979,7 @@ public class WifiNetworkSelector { mLocalLog = localLog; mWifiMetrics = wifiMetrics; mWifiNative = wifiNative; - + mThroughputPredictor = throughputPredictor; mEnableAutoJoinWhenAssociated = context.getResources().getBoolean( R.bool.config_wifi_framework_enable_associated_network_selection); mStayOnNetworkMinimumTxRate = context.getResources().getInteger( diff --git a/service/res/values/config.xml b/service/res/values/config.xml index ba65ba08c..0b3623229 100644 --- a/service/res/values/config.xml +++ b/service/res/values/config.xml @@ -205,6 +205,15 @@ <!-- Indicates that wifi link probing is supported on this device --> <bool translatable="false" name="config_wifi_link_probing_supported">false</bool> + <!-- Indicates that 11ax mode is supported on this device --> + <bool translatable="false" name="config_wifi_11ax_supported">false</bool> + + <!-- Indicates that contiguous 160MHz mode is supported on this device --> + <bool translatable="false" name="config_wifi_contiguous_160mhz_supported">false</bool> + + <!-- Integer indicating the max number of spatial streams supported on this device --> + <integer translatable="false" name="config_wifi_max_num_spatial_stream_supported">2</integer> + <!-- Configure wifi tcp buffersizes in the form: rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max --> <string name="config_wifi_tcp_buffers" translatable="false">524288,1048576,2097152,262144,524288,1048576</string> diff --git a/service/res/values/overlayable.xml b/service/res/values/overlayable.xml index 5d20c24c9..909a669eb 100644 --- a/service/res/values/overlayable.xml +++ b/service/res/values/overlayable.xml @@ -91,6 +91,9 @@ <item type="bool" name="config_wifi_ap_mac_randomization_supported" /> <item type="bool" name="config_wifi_aggressive_randomization_ssid_whitelist_enabled" /> <item type="bool" name="config_wifi_link_probing_supported" /> + <item type="bool" name="config_wifi_11ax_supported" /> + <item type="bool" name="config_wifi_contiguous_160mhz_supported" /> + <item type="integer" name="config_wifi_max_num_spatial_stream_supported" /> <item type="string" name="config_wifi_tcp_buffers" /> <item type="string" name="wifi_tether_configure_ssid_default" /> <item type="string" name="wifi_localhotspot_configure_ssid_default" /> |