1 /*
2  * Copyright 2018 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 
17 package com.android.server.wifi;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.net.wifi.ScanResult;
22 import android.net.wifi.WifiInfo;
23 import android.net.wifi.WifiNetworkSelectionConfig;
24 import android.os.Build;
25 import android.text.TextUtils;
26 import android.util.Log;
27 import android.util.SparseArray;
28 
29 import androidx.annotation.RequiresApi;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.modules.utils.build.SdkLevel;
33 import com.android.server.wifi.util.KeyValueListParser;
34 import com.android.wifi.resources.R;
35 
36 /**
37  * Holds parameters used for scoring networks.
38  *
39  * Doing this in one place means that there's a better chance of consistency between
40  * connected score and network selection.
41  *
42  */
43 public class ScoringParams {
44     private final Context mContext;
45 
46     private static final String TAG = "WifiScoringParams";
47     private static final int EXIT = 0;
48     private static final int ENTRY = 1;
49     private static final int SUFFICIENT = 2;
50     private static final int GOOD = 3;
51 
52     private static final int ACTIVE_TRAFFIC = 1;
53     private static final int HIGH_TRAFFIC = 2;
54 
55     @VisibleForTesting
56     public static final int FREQUENCY_WEIGHT_LOW = -40;
57     @VisibleForTesting
58     public static final int FREQUENCY_WEIGHT_DEFAULT = 0;
59     @VisibleForTesting
60     public static final int FREQUENCY_WEIGHT_HIGH = 40;
61 
62     SparseArray<Integer> mFrequencyWeights = new SparseArray<>();
63 
64     /**
65      * Parameter values are stored in a separate container so that a new collection of values can
66      * be checked for consistency before activating them.
67      */
68     private class Values {
69         /** RSSI thresholds for 2.4 GHz band (dBm) */
70         public static final String KEY_RSSI2 = "rssi2";
71         public final int[] rssi2 = {-83, -80, -73, -60};
72 
73         /** RSSI thresholds for 5 GHz band (dBm) */
74         public static final String KEY_RSSI5 = "rssi5";
75         public final int[] rssi5 = {-80, -77, -70, -57};
76 
77         /** RSSI thresholds for 6 GHz band (dBm) */
78         public static final String KEY_RSSI6 = "rssi6";
79         public final int[] rssi6 = {-80, -77, -70, -57};
80 
81        /** Guidelines based on packet rates (packets/sec) */
82         public static final String KEY_PPS = "pps";
83         public final int[] pps = {0, 1, 100};
84 
85         /** Number of seconds for RSSI forecast */
86         public static final String KEY_HORIZON = "horizon";
87         public static final int MIN_HORIZON = -9;
88         public static final int MAX_HORIZON = 60;
89         public int horizon = 15;
90 
91         /** Number 0-10 influencing requests for network unreachability detection */
92         public static final String KEY_NUD = "nud";
93         public static final int MIN_NUD = 0;
94         public static final int MAX_NUD = 10;
95         public int nud = 8;
96 
97         /** Experiment identifier */
98         public static final String KEY_EXPID = "expid";
99         public static final int MIN_EXPID = 0;
100         public static final int MAX_EXPID = Integer.MAX_VALUE;
101         public int expid = 0;
102 
103         /** CandidateScorer parameters */
104         public int throughputBonusNumerator = 120;
105         public int throughputBonusDenominator = 433;
106         public int throughputBonusNumeratorAfter800Mbps = 1;
107         public int throughputBonusDenominatorAfter800Mbps = 16;
108         public boolean enable6GhzBeaconRssiBoost = true;
109         public int throughputBonusLimit = 320;
110         public int savedNetworkBonus = 500;
111         public int unmeteredNetworkBonus = 1000;
112         public int currentNetworkBonusMin = 16;
113         public int currentNetworkBonusPercent = 20;
114         public int secureNetworkBonus = 40;
115         public int band6GhzBonus = 0;
116         public int scoringBucketStepSize = 500;
117         public int lastUnmeteredSelectionMinutes = 480;
118         public int lastMeteredSelectionMinutes = 120;
119         public int estimateRssiErrorMargin = 5;
120         public static final int MIN_MINUTES = 1;
121         public static final int MAX_MINUTES = Integer.MAX_VALUE / (60 * 1000);
122 
Values()123         Values() {
124         }
125 
Values(Values source)126         Values(Values source) {
127             for (int i = 0; i < rssi2.length; i++) {
128                 rssi2[i] = source.rssi2[i];
129             }
130             for (int i = 0; i < rssi5.length; i++) {
131                 rssi5[i] = source.rssi5[i];
132             }
133             for (int i = 0; i < rssi6.length; i++) {
134                 rssi6[i] = source.rssi6[i];
135             }
136             for (int i = 0; i < pps.length; i++) {
137                 pps[i] = source.pps[i];
138             }
139             horizon = source.horizon;
140             nud = source.nud;
141             expid = source.expid;
142         }
143 
validate()144         public void validate() throws IllegalArgumentException {
145             validateRssiArray(rssi2);
146             validateRssiArray(rssi5);
147             validateRssiArray(rssi6);
148             validateOrderedNonNegativeArray(pps);
149             validateRange(horizon, MIN_HORIZON, MAX_HORIZON);
150             validateRange(nud, MIN_NUD, MAX_NUD);
151             validateRange(expid, MIN_EXPID, MAX_EXPID);
152             validateRange(lastUnmeteredSelectionMinutes, MIN_MINUTES, MAX_MINUTES);
153             validateRange(lastMeteredSelectionMinutes, MIN_MINUTES, MAX_MINUTES);
154         }
155 
validateRssiArray(int[] rssi)156         private void validateRssiArray(int[] rssi) throws IllegalArgumentException {
157             int low = WifiInfo.MIN_RSSI;
158             int high = Math.min(WifiInfo.MAX_RSSI, -1); // Stricter than Wifiinfo
159             for (int i = 0; i < rssi.length; i++) {
160                 validateRange(rssi[i], low, high);
161                 low = rssi[i];
162             }
163         }
164 
validateRange(int k, int low, int high)165         private void validateRange(int k, int low, int high) throws IllegalArgumentException {
166             if (k < low || k > high) {
167                 throw new IllegalArgumentException();
168             }
169         }
170 
validateOrderedNonNegativeArray(int[] a)171         private void validateOrderedNonNegativeArray(int[] a) throws IllegalArgumentException {
172             int low = 0;
173             for (int i = 0; i < a.length; i++) {
174                 if (a[i] < low) {
175                     throw new IllegalArgumentException();
176                 }
177                 low = a[i];
178             }
179         }
180 
parseString(String kvList)181         public void parseString(String kvList) throws IllegalArgumentException {
182             KeyValueListParser parser = new KeyValueListParser(',');
183             parser.setString(kvList);
184             if (parser.size() != ("" + kvList).split(",").length) {
185                 throw new IllegalArgumentException("dup keys");
186             }
187             updateIntArray(rssi2, parser, KEY_RSSI2);
188             updateIntArray(rssi5, parser, KEY_RSSI5);
189             updateIntArray(rssi6, parser, KEY_RSSI6);
190             updateIntArray(pps, parser, KEY_PPS);
191             horizon = updateInt(parser, KEY_HORIZON, horizon);
192             nud = updateInt(parser, KEY_NUD, nud);
193             expid = updateInt(parser, KEY_EXPID, expid);
194         }
195 
updateInt(KeyValueListParser parser, String key, int defaultValue)196         private int updateInt(KeyValueListParser parser, String key, int defaultValue)
197                 throws IllegalArgumentException {
198             String value = parser.getString(key, null);
199             if (value == null) return defaultValue;
200             try {
201                 return Integer.parseInt(value);
202             } catch (NumberFormatException e) {
203                 throw new IllegalArgumentException();
204             }
205         }
206 
updateIntArray(final int[] dest, KeyValueListParser parser, String key)207         private void updateIntArray(final int[] dest, KeyValueListParser parser, String key)
208                 throws IllegalArgumentException {
209             if (parser.getString(key, null) == null) return;
210             int[] ints = parser.getIntArray(key, null);
211             if (ints == null) throw new IllegalArgumentException();
212             if (ints.length != dest.length) throw new IllegalArgumentException();
213             for (int i = 0; i < dest.length; i++) {
214                 dest[i] = ints[i];
215             }
216         }
217 
218         @Override
toString()219         public String toString() {
220             StringBuilder sb = new StringBuilder();
221             appendKey(sb, KEY_RSSI2);
222             appendInts(sb, rssi2);
223             appendKey(sb, KEY_RSSI5);
224             appendInts(sb, rssi5);
225             appendKey(sb, KEY_RSSI6);
226             appendInts(sb, rssi6);
227             appendKey(sb, KEY_PPS);
228             appendInts(sb, pps);
229             appendKey(sb, KEY_HORIZON);
230             sb.append(horizon);
231             appendKey(sb, KEY_NUD);
232             sb.append(nud);
233             appendKey(sb, KEY_EXPID);
234             sb.append(expid);
235             return sb.toString();
236         }
237 
appendKey(StringBuilder sb, String key)238         private void appendKey(StringBuilder sb, String key) {
239             if (sb.length() != 0) sb.append(",");
240             sb.append(key).append("=");
241         }
242 
appendInts(StringBuilder sb, final int[] a)243         private void appendInts(StringBuilder sb, final int[] a) {
244             final int n = a.length;
245             for (int i = 0; i < n; i++) {
246                 if (i > 0) sb.append(":");
247                 sb.append(a[i]);
248             }
249         }
250     }
251 
252     @NonNull private Values mVal = null;
253 
254     @VisibleForTesting
ScoringParams()255     public ScoringParams() {
256         mContext = null;
257         mVal = new Values();
258     }
259 
ScoringParams(Context context)260     public ScoringParams(Context context) {
261         mContext = context;
262         loadResources(mContext);
263     }
264 
loadResources(Context context)265     private void loadResources(Context context) {
266         if (mVal != null) return;
267         mVal = new Values();
268         mVal.rssi2[EXIT] = context.getResources().getInteger(
269                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
270         mVal.rssi2[ENTRY] = context.getResources().getInteger(
271                 R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz);
272         mVal.rssi2[SUFFICIENT] = context.getResources().getInteger(
273                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
274         mVal.rssi2[GOOD] = context.getResources().getInteger(
275                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
276         mVal.rssi5[EXIT] = context.getResources().getInteger(
277                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
278         mVal.rssi5[ENTRY] = context.getResources().getInteger(
279                 R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz);
280         mVal.rssi5[SUFFICIENT] = context.getResources().getInteger(
281                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
282         mVal.rssi5[GOOD] = context.getResources().getInteger(
283                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
284         mVal.rssi6[EXIT] = context.getResources().getInteger(
285                 R.integer.config_wifiFrameworkScoreBadRssiThreshold6ghz);
286         mVal.rssi6[ENTRY] = context.getResources().getInteger(
287                 R.integer.config_wifiFrameworkScoreEntryRssiThreshold6ghz);
288         mVal.rssi6[SUFFICIENT] = context.getResources().getInteger(
289                 R.integer.config_wifiFrameworkScoreLowRssiThreshold6ghz);
290         mVal.rssi6[GOOD] = context.getResources().getInteger(
291                 R.integer.config_wifiFrameworkScoreGoodRssiThreshold6ghz);
292         mVal.throughputBonusNumerator = context.getResources().getInteger(
293                 R.integer.config_wifiFrameworkThroughputBonusNumerator);
294         mVal.throughputBonusDenominator = context.getResources().getInteger(
295                 R.integer.config_wifiFrameworkThroughputBonusDenominator);
296         mVal.throughputBonusNumeratorAfter800Mbps = context.getResources().getInteger(
297                 R.integer.config_wifiFrameworkThroughputBonusNumeratorAfter800Mbps);
298         mVal.throughputBonusDenominatorAfter800Mbps = context.getResources().getInteger(
299                 R.integer.config_wifiFrameworkThroughputBonusDenominatorAfter800Mbps);
300         mVal.enable6GhzBeaconRssiBoost = context.getResources().getBoolean(
301                 R.bool.config_wifiEnable6GhzBeaconRssiBoost);
302         mVal.throughputBonusLimit = context.getResources().getInteger(
303                 R.integer.config_wifiFrameworkThroughputBonusLimit);
304         mVal.savedNetworkBonus = context.getResources().getInteger(
305                 R.integer.config_wifiFrameworkSavedNetworkBonus);
306         mVal.unmeteredNetworkBonus = context.getResources().getInteger(
307                 R.integer.config_wifiFrameworkUnmeteredNetworkBonus);
308         mVal.currentNetworkBonusMin = context.getResources().getInteger(
309                 R.integer.config_wifiFrameworkCurrentNetworkBonusMin);
310         mVal.currentNetworkBonusPercent = context.getResources().getInteger(
311             R.integer.config_wifiFrameworkCurrentNetworkBonusPercent);
312         mVal.secureNetworkBonus = context.getResources().getInteger(
313                 R.integer.config_wifiFrameworkSecureNetworkBonus);
314         mVal.band6GhzBonus = context.getResources().getInteger(R.integer.config_wifiBand6GhzBonus);
315         mVal.scoringBucketStepSize = context.getResources().getInteger(
316                 R.integer.config_wifiScoringBucketStepSize);
317         mVal.lastUnmeteredSelectionMinutes = context.getResources().getInteger(
318                 R.integer.config_wifiFrameworkLastSelectionMinutes);
319         mVal.lastMeteredSelectionMinutes = context.getResources().getInteger(
320                 R.integer.config_wifiFrameworkLastMeteredSelectionMinutes);
321         mVal.estimateRssiErrorMargin = context.getResources().getInteger(
322                 R.integer.config_wifiEstimateRssiErrorMarginDb);
323         mVal.pps[ACTIVE_TRAFFIC] = context.getResources().getInteger(
324                 R.integer.config_wifiFrameworkMinPacketPerSecondActiveTraffic);
325         mVal.pps[HIGH_TRAFFIC] = context.getResources().getInteger(
326                 R.integer.config_wifiFrameworkMinPacketPerSecondHighTraffic);
327         try {
328             mVal.validate();
329         } catch (IllegalArgumentException e) {
330             Log.wtf(TAG, "Inconsistent config_wifi_framework_ resources: " + this, e);
331         }
332     }
333 
334     private static final String COMMA_KEY_VAL_STAR = "^(,[A-Za-z_][A-Za-z0-9_]*=[0-9.:+-]+)*$";
335 
336     /**
337      * Updates the parameters from the given parameter string.
338      * If any errors are detected, no change is made.
339      * @param kvList is a comma-separated key=value list.
340      * @return true for success
341      */
342     @VisibleForTesting
update(String kvList)343     public boolean update(String kvList) {
344         if (TextUtils.isEmpty(kvList)) {
345             return true;
346         }
347         if (!("," + kvList).matches(COMMA_KEY_VAL_STAR)) {
348             return false;
349         }
350         Values v = new Values(mVal);
351         try {
352             v.parseString(kvList);
353             v.validate();
354             mVal = v;
355             return true;
356         } catch (IllegalArgumentException e) {
357             return false;
358         }
359     }
360 
361     /**
362      * Sanitize a string to make it safe for printing.
363      * @param params is the untrusted string
364      * @return string with questionable characters replaced with question marks
365      */
sanitize(String params)366     public String sanitize(String params) {
367         if (params == null) return "";
368         String printable = params.replaceAll("[^A-Za-z_0-9=,:.+-]", "?");
369         if (printable.length() > 100) {
370             printable = printable.substring(0, 98) + "...";
371         }
372         return printable;
373     }
374 
375     /**
376      * Returns the RSSI value at which the connection is deemed to be unusable,
377      * in the absence of other indications.
378      */
getExitRssi(int frequencyMegaHertz)379     public int getExitRssi(int frequencyMegaHertz) {
380         return getRssiArray(frequencyMegaHertz)[EXIT];
381     }
382 
383     /**
384      * Returns the minimum scan RSSI for making a connection attempt.
385      */
getEntryRssi(int frequencyMegaHertz)386     public int getEntryRssi(int frequencyMegaHertz) {
387         return getRssiArray(frequencyMegaHertz)[ENTRY];
388     }
389 
390     /**
391      * Returns a connected RSSI value that indicates the connection is
392      * good enough that we needn't scan for alternatives.
393      */
getSufficientRssi(int frequencyMegaHertz)394     public int getSufficientRssi(int frequencyMegaHertz) {
395         return getRssiArray(frequencyMegaHertz)[SUFFICIENT];
396     }
397 
398     /**
399      * Returns a connected RSSI value that indicates a good connection.
400      */
getGoodRssi(int frequencyMegaHertz)401     public int getGoodRssi(int frequencyMegaHertz) {
402         return getRssiArray(frequencyMegaHertz)[GOOD];
403     }
404 
405     /**
406      * Sets the RSSI thresholds for 2.4 GHz.
407      */
408     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setRssi2Thresholds(int[] rssi2)409     public void setRssi2Thresholds(int[] rssi2) {
410         if (WifiNetworkSelectionConfig.isRssiThresholdResetArray(rssi2)) {
411             mVal.rssi2[EXIT] = mContext.getResources().getInteger(
412                     R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
413             mVal.rssi2[ENTRY] = mContext.getResources().getInteger(
414                     R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz);
415             mVal.rssi2[SUFFICIENT] = mContext.getResources().getInteger(
416                     R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
417             mVal.rssi2[GOOD] = mContext.getResources().getInteger(
418                     R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
419         } else {
420             mVal.rssi2[EXIT] = rssi2[EXIT];
421             mVal.rssi2[ENTRY] = rssi2[ENTRY];
422             mVal.rssi2[SUFFICIENT] = rssi2[SUFFICIENT];
423             mVal.rssi2[GOOD] = rssi2[GOOD];
424         }
425     }
426 
427     /**
428      * Sets the RSSI thresholds for 5 GHz.
429      */
430     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setRssi5Thresholds(int[] rssi5)431     public void setRssi5Thresholds(int[] rssi5) {
432         if (WifiNetworkSelectionConfig.isRssiThresholdResetArray(rssi5)) {
433             mVal.rssi5[EXIT] = mContext.getResources().getInteger(
434                     R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
435             mVal.rssi5[ENTRY] = mContext.getResources().getInteger(
436                     R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz);
437             mVal.rssi5[SUFFICIENT] = mContext.getResources().getInteger(
438                     R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
439             mVal.rssi5[GOOD] = mContext.getResources().getInteger(
440                     R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
441         } else {
442             mVal.rssi5[EXIT] = rssi5[EXIT];
443             mVal.rssi5[ENTRY] = rssi5[ENTRY];
444             mVal.rssi5[SUFFICIENT] = rssi5[SUFFICIENT];
445             mVal.rssi5[GOOD] = rssi5[GOOD];
446         }
447     }
448 
449     /**
450      * Sets the RSSI thresholds for 6 GHz.
451      */
452     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setRssi6Thresholds(int[] rssi6)453     public void setRssi6Thresholds(int[] rssi6) {
454         if (WifiNetworkSelectionConfig.isRssiThresholdResetArray(rssi6)) {
455             mVal.rssi6[EXIT] = mContext.getResources().getInteger(
456                     R.integer.config_wifiFrameworkScoreBadRssiThreshold6ghz);
457             mVal.rssi6[ENTRY] = mContext.getResources().getInteger(
458                     R.integer.config_wifiFrameworkScoreEntryRssiThreshold6ghz);
459             mVal.rssi6[SUFFICIENT] = mContext.getResources().getInteger(
460                     R.integer.config_wifiFrameworkScoreLowRssiThreshold6ghz);
461             mVal.rssi6[GOOD] = mContext.getResources().getInteger(
462                     R.integer.config_wifiFrameworkScoreGoodRssiThreshold6ghz);
463         } else {
464             mVal.rssi6[EXIT] = rssi6[EXIT];
465             mVal.rssi6[ENTRY] = rssi6[ENTRY];
466             mVal.rssi6[SUFFICIENT] = rssi6[SUFFICIENT];
467             mVal.rssi6[GOOD] = rssi6[GOOD];
468         }
469     }
470 
471     /**
472      * Sets the frequency weights list
473      */
setFrequencyWeights(SparseArray<Integer> weights)474     public void setFrequencyWeights(SparseArray<Integer> weights) {
475         mFrequencyWeights = weights;
476     }
477 
478     /**
479      * Returns the frequency weight score for the provided frequency
480      */
getFrequencyScore(int frequencyMegaHertz)481     public int getFrequencyScore(int frequencyMegaHertz) {
482         if (SdkLevel.isAtLeastT() && mFrequencyWeights.contains(frequencyMegaHertz)) {
483             switch(mFrequencyWeights.get(frequencyMegaHertz)) {
484                 case WifiNetworkSelectionConfig.FREQUENCY_WEIGHT_LOW:
485                     return FREQUENCY_WEIGHT_LOW;
486                 case WifiNetworkSelectionConfig.FREQUENCY_WEIGHT_HIGH:
487                     return FREQUENCY_WEIGHT_HIGH;
488                 default:
489                     // This should never happen because we've validated the frequency weights
490                     // in WifiNetworkSectionConfig.
491                     Log.wtf(TAG, "Invalid frequency weight type "
492                             + mFrequencyWeights.get(frequencyMegaHertz));
493             }
494         }
495         return FREQUENCY_WEIGHT_DEFAULT;
496     }
497 
498     /**
499      * Returns the number of seconds to use for rssi forecast.
500      */
getHorizonSeconds()501     public int getHorizonSeconds() {
502         return mVal.horizon;
503     }
504 
505     /**
506      * Returns a packet rate that should be considered acceptable for staying on wifi,
507      * no matter how bad the RSSI gets (packets per second).
508      */
getYippeeSkippyPacketsPerSecond()509     public int getYippeeSkippyPacketsPerSecond() {
510         return mVal.pps[HIGH_TRAFFIC];
511     }
512 
513     /**
514      * Returns a packet rate that should be considered acceptable to skip scan or network selection
515      */
getActiveTrafficPacketsPerSecond()516     public int getActiveTrafficPacketsPerSecond() {
517         return mVal.pps[ACTIVE_TRAFFIC];
518     }
519 
520     /**
521      * Returns a number between 0 and 10 inclusive that indicates
522      * how aggressive to be about asking for IP configuration checks
523      * (also known as Network Unreachabilty Detection, or NUD).
524      *
525      * 0 - no nud checks requested by scorer (framework still checks after roam)
526      * 1 - check when score becomes very low
527      *     ...
528      * 10 - check when score first breaches threshold, and again as it gets worse
529      *
530      */
getNudKnob()531     public int getNudKnob() {
532         return mVal.nud;
533     }
534 
535     /**
536      * Returns the estimate rssi error margin to account minor differences in the environment
537      * and the device's orientation.
538      *
539      */
getEstimateRssiErrorMargin()540     public int getEstimateRssiErrorMargin() {
541         return mVal.estimateRssiErrorMargin;
542     }
543 
544     /**
545      */
getThroughputBonusNumerator()546     public int getThroughputBonusNumerator() {
547         return mVal.throughputBonusNumerator;
548     }
549 
550     /**
551      */
getThroughputBonusDenominator()552     public int getThroughputBonusDenominator() {
553         return mVal.throughputBonusDenominator;
554     }
555 
556     /**
557      * Getter for throughput numerator after 800Mbps.
558      */
getThroughputBonusNumeratorAfter800Mbps()559     public int getThroughputBonusNumeratorAfter800Mbps() {
560         return mVal.throughputBonusNumeratorAfter800Mbps;
561     }
562 
563     /**
564      * Getter for throughput denominator after 800Mbps.
565      */
getThroughputBonusDenominatorAfter800Mbps()566     public int getThroughputBonusDenominatorAfter800Mbps() {
567         return mVal.throughputBonusDenominatorAfter800Mbps;
568     }
569 
570     /**
571      * Feature flag for boosting 6Ghz RSSI based on channel width.
572      */
is6GhzBeaconRssiBoostEnabled()573     public boolean is6GhzBeaconRssiBoostEnabled() {
574         return mVal.enable6GhzBeaconRssiBoost;
575     }
576 
577     /*
578      * Returns the maximum bonus for the network selection candidate score
579      * for the contribution of the selected score.
580      */
getThroughputBonusLimit()581     public int getThroughputBonusLimit() {
582         return mVal.throughputBonusLimit;
583     }
584 
585     /*
586      * Returns the bonus for the network selection candidate score
587      * for a saved network (i.e., not a suggestion).
588      */
getSavedNetworkBonus()589     public int getSavedNetworkBonus() {
590         return mVal.savedNetworkBonus;
591     }
592 
593     /*
594      * Returns the bonus for the network selection candidate score
595      * for an unmetered network.
596      */
getUnmeteredNetworkBonus()597     public int getUnmeteredNetworkBonus() {
598         return mVal.unmeteredNetworkBonus;
599     }
600 
601     /*
602      * Returns the minimum bonus for the network selection candidate score
603      * for the currently connected network.
604      */
getCurrentNetworkBonusMin()605     public int getCurrentNetworkBonusMin() {
606         return mVal.currentNetworkBonusMin;
607     }
608 
609     /*
610      * Returns the percentage bonus for the network selection candidate score
611      * for the currently connected network. The percent value is applied to rssi score and
612      * throughput score;
613      */
getCurrentNetworkBonusPercent()614     public int getCurrentNetworkBonusPercent() {
615         return mVal.currentNetworkBonusPercent;
616     }
617 
618     /*
619      * Returns the bonus for the network selection candidate score
620      * for a secure network.
621      */
getSecureNetworkBonus()622     public int getSecureNetworkBonus() {
623         return mVal.secureNetworkBonus;
624     }
625 
626     /**
627      * Returns the bonus given if the network belongs to the 6Ghz band.
628      */
getBand6GhzBonus()629     public int getBand6GhzBonus() {
630         return mVal.band6GhzBonus;
631     }
632 
633     /**
634      * Returns the expected amount of score to reach the next tier during candidate scoring. This
635      * value should be configured according to the value of parameters that determine the
636      * scoring buckets such as {@code config_wifiFrameworkSavedNetworkBonus} and
637      * {@code config_wifiFrameworkUnmeteredNetworkBonus}.
638      */
getScoringBucketStepSize()639     public int getScoringBucketStepSize() {
640         return mVal.scoringBucketStepSize;
641     }
642 
643     /*
644      * Returns the duration in minutes for a recently selected non-metered network
645      * to be strongly favored.
646      */
getLastUnmeteredSelectionMinutes()647     public int getLastUnmeteredSelectionMinutes() {
648         return mVal.lastUnmeteredSelectionMinutes;
649     }
650 
651     /*
652      * Returns the duration in minutes for a recently selected metered network
653      * to be strongly favored.
654      */
getLastMeteredSelectionMinutes()655     public int getLastMeteredSelectionMinutes() {
656         return mVal.lastMeteredSelectionMinutes;
657     }
658 
659     /**
660      * Returns the experiment identifier.
661      *
662      * This value may be used to tag a set of experimental settings.
663      */
getExperimentIdentifier()664     public int getExperimentIdentifier() {
665         return mVal.expid;
666     }
667 
668     /**
669      * Returns the RSSI thresholds array for the input band.
670      */
getRssiArray(int frequency)671     public int[] getRssiArray(int frequency) {
672         if (ScanResult.is24GHz(frequency)) {
673             return mVal.rssi2;
674         } else if (ScanResult.is5GHz(frequency)) {
675             return mVal.rssi5;
676         } else if (ScanResult.is6GHz(frequency)) {
677             return mVal.rssi6;
678         }
679         // Invalid frequency use
680         Log.e(TAG, "Invalid frequency(" + frequency + "), using 5G as default rssi array");
681         return mVal.rssi5;
682     }
683 
684     @Override
toString()685     public String toString() {
686         return mVal.toString();
687     }
688 }
689