1 /*
2  * Copyright (C) 2022 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 android.net.wifi;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.net.wifi.ScanResult.WifiBand;
23 import android.os.Build;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.SparseArray;
27 
28 import androidx.annotation.RequiresApi;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.Arrays;
33 import java.util.Objects;
34 import java.util.function.Consumer;
35 
36 /**
37  * An Object used in {@link WifiManager#setNetworkSelectionConfig(WifiNetworkSelectionConfig)}.
38  * @hide
39  */
40 @SystemApi
41 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
42 public final class WifiNetworkSelectionConfig implements Parcelable {
43     /** @hide */
44     @Retention(RetentionPolicy.SOURCE)
45     @IntDef(prefix = {"ASSOCIATED_NETWORK_SELECTION_OVERRIDE_"}, value = {
46             ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE,
47             ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED,
48             ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED})
49     public @interface AssociatedNetworkSelectionOverride {}
50     /**
51      * A constant used in {@link Builder#setAssociatedNetworkSelectionOverride(int)}
52      * This is the default value which performs no override.
53      */
54     public static final int ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE = 0;
55     /**
56      * A constant used in {{@link Builder#setAssociatedNetworkSelectionOverride(int)}
57      * Overrides the config_wifi_framework_enable_associated_network_selection overlay to true to
58      * allow the wifi framework to automatically select and switch to a better wifi network while
59      * already connected.
60      */
61     public static final int ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED = 1;
62     /**
63      * A constant used in {@link Builder#setAssociatedNetworkSelectionOverride(int)}
64      * Overrides the config_wifi_framework_enable_associated_network_selection overlay to false to
65      * disallow the wifi framework to automatically select and connect to another network while
66      * already connected.
67      */
68     public static final int ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED = 2;
69 
70     /** @hide */
71     @Retention(RetentionPolicy.SOURCE)
72     @IntDef(prefix = {"FREQUENCY_WEIGHT_"}, value = {
73             FREQUENCY_WEIGHT_LOW,
74             FREQUENCY_WEIGHT_HIGH})
75     public @interface FrequencyWeight {}
76 
77     /**
78      * A constant used in {@link Builder#setFrequencyWeights(SparseArray)} to indicate a low
79      * preference for the frequency it's associated with.
80      */
81     public static final int FREQUENCY_WEIGHT_LOW = 0;
82     /**
83      * A constant used in {@link Builder#setFrequencyWeights(SparseArray)} to indicate a high
84      * preference for the frequency it's associated with.
85      */
86     public static final int FREQUENCY_WEIGHT_HIGH = 1;
87 
88     private boolean mSufficiencyCheckEnabledWhenScreenOff = true;
89     private boolean mSufficiencyCheckEnabledWhenScreenOn = true;
90     private boolean mUserConnectChoiceOverrideEnabled = true;
91     private boolean mLastSelectionWeightEnabled = true;
92     private int mAssociatedNetworkSelectionOverride = ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE;
93 
94     /** RSSI thresholds for 2.4 GHz band (dBm) */
95     private int[] mRssi2Thresholds = new int[4];
96 
97     /** RSSI thresholds for 5 GHz band (dBm) */
98     private int[] mRssi5Thresholds = new int[4];
99 
100     /** RSSI thresholds for 6 GHz band (dBm) */
101     private int[] mRssi6Thresholds = new int[4];
102 
103     /** Frequency weight list */
104     private SparseArray<Integer> mFrequencyWeights = new SparseArray<>();
105 
106     // empty constructor
WifiNetworkSelectionConfig()107     private WifiNetworkSelectionConfig() {
108 
109     }
110 
111     // copy constructor used by Builder
WifiNetworkSelectionConfig(WifiNetworkSelectionConfig that)112     private WifiNetworkSelectionConfig(WifiNetworkSelectionConfig that) {
113         mSufficiencyCheckEnabledWhenScreenOff = that.mSufficiencyCheckEnabledWhenScreenOff;
114         mSufficiencyCheckEnabledWhenScreenOn = that.mSufficiencyCheckEnabledWhenScreenOn;
115         mAssociatedNetworkSelectionOverride = that.mAssociatedNetworkSelectionOverride;
116         mUserConnectChoiceOverrideEnabled = that.mUserConnectChoiceOverrideEnabled;
117         mLastSelectionWeightEnabled = that.mLastSelectionWeightEnabled;
118         mRssi2Thresholds = that.mRssi2Thresholds;
119         mRssi5Thresholds = that.mRssi5Thresholds;
120         mRssi6Thresholds = that.mRssi6Thresholds;
121         mFrequencyWeights = that.mFrequencyWeights;
122     }
123 
124     /**
125      * See {@link Builder#setSufficiencyCheckEnabledWhenScreenOff(boolean)}.
126      */
isSufficiencyCheckEnabledWhenScreenOff()127     public boolean isSufficiencyCheckEnabledWhenScreenOff() {
128         return mSufficiencyCheckEnabledWhenScreenOff;
129     }
130 
131     /**
132      * See {@link Builder#setSufficiencyCheckEnabledWhenScreenOn(boolean)}.
133      */
isSufficiencyCheckEnabledWhenScreenOn()134     public boolean isSufficiencyCheckEnabledWhenScreenOn() {
135         return mSufficiencyCheckEnabledWhenScreenOn;
136     }
137 
138     /**
139      * See {@link Builder#setUserConnectChoiceOverrideEnabled(boolean)}.
140      */
isUserConnectChoiceOverrideEnabled()141     public boolean isUserConnectChoiceOverrideEnabled() {
142         return mUserConnectChoiceOverrideEnabled;
143     }
144 
145     /**
146      * See {@link Builder#setLastSelectionWeightEnabled(boolean)}.
147      */
isLastSelectionWeightEnabled()148     public boolean isLastSelectionWeightEnabled() {
149         return mLastSelectionWeightEnabled;
150     }
151 
152     /**
153      * See {@link Builder#setAssociatedNetworkSelectionOverride(int)}.
154      */
getAssociatedNetworkSelectionOverride()155     public @AssociatedNetworkSelectionOverride int getAssociatedNetworkSelectionOverride() {
156         return mAssociatedNetworkSelectionOverride;
157     }
158 
isValidAssociatedNetworkSelectionOverride(int override)159     private static boolean isValidAssociatedNetworkSelectionOverride(int override) {
160         return override >= ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE
161                 && override <= ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED;
162     }
163 
isValidBand(@ifiBand int band)164     private static boolean isValidBand(@WifiBand int band) {
165         switch (band) {
166             case ScanResult.WIFI_BAND_24_GHZ:
167             case ScanResult.WIFI_BAND_5_GHZ:
168             case ScanResult.WIFI_BAND_6_GHZ:
169                 return true;
170             default:
171                 return false;
172         }
173     }
174 
isValidRssiThresholdArray(int[] thresholds)175     private static boolean isValidRssiThresholdArray(int[] thresholds) {
176         if (thresholds == null || thresholds.length != 4) return false;
177 
178         if (!isRssiThresholdResetArray(thresholds)) {
179             int low = WifiInfo.MIN_RSSI - 1;
180             int high = Math.min(WifiInfo.MAX_RSSI, -1);
181             for (int i = 0; i < thresholds.length; i++) {
182                 if (thresholds[i] <= low || thresholds[i] > high) {
183                     return false;
184                 }
185                 low = thresholds[i];
186             }
187         }
188         return true;
189     }
190 
isValidFrequencyWeightArray(SparseArray<Integer> weights)191     private static boolean isValidFrequencyWeightArray(SparseArray<Integer> weights) {
192         if (weights == null) return false;
193 
194         for (int i = 0; i < weights.size(); i++) {
195             int value = weights.valueAt(i);
196             if (value < FREQUENCY_WEIGHT_LOW || value > FREQUENCY_WEIGHT_HIGH) return false;
197         }
198         return true;
199     }
200 
201     /**
202      * Check whether the given RSSI threshold array contains all 0s.
203      * @hide
204      */
isRssiThresholdResetArray(@onNull int[] thresholds)205     public static boolean isRssiThresholdResetArray(@NonNull int[] thresholds) {
206         for (int value : thresholds) {
207             if (value != 0) return false;
208         }
209         return true;
210     }
211 
212     /**
213      * Check whether the current configuration is valid.
214      * @hide
215      */
isValid()216     public boolean isValid() {
217         return isValidAssociatedNetworkSelectionOverride(mAssociatedNetworkSelectionOverride)
218                 && isValidRssiThresholdArray(mRssi2Thresholds)
219                 && isValidRssiThresholdArray(mRssi5Thresholds)
220                 && isValidRssiThresholdArray(mRssi6Thresholds)
221                 && isValidFrequencyWeightArray(mFrequencyWeights);
222     }
223 
224     /**
225      * See {@link Builder#setRssiThresholds(int, int[])}.
226      * Returns RSSI thresholds for the input band.
227      *
228      * @throws IllegalArgumentException if the input band is not a supported {@link WifiBand}
229      */
getRssiThresholds(@ifiBand int band)230     public @NonNull int[] getRssiThresholds(@WifiBand int band) {
231         if (!isValidBand(band)) {
232             throw new IllegalArgumentException("Invalid band=" + band);
233         }
234         switch (band) {
235             case ScanResult.WIFI_BAND_24_GHZ:
236                 return mRssi2Thresholds;
237             case ScanResult.WIFI_BAND_5_GHZ:
238                 return mRssi5Thresholds;
239             case ScanResult.WIFI_BAND_6_GHZ:
240                 return mRssi6Thresholds;
241         }
242         throw new IllegalArgumentException("Did not find RSSI thresholds for band=" + band);
243     }
244 
245     /**
246      * See {@link Builder#setFrequencyWeights(SparseArray)}.
247      */
getFrequencyWeights()248     public @NonNull SparseArray<Integer> getFrequencyWeights() {
249         return mFrequencyWeights;
250     }
251 
252     /**
253      * Used to create a {@link WifiNetworkSelectionConfig} Object.
254      */
255     public static final class Builder {
256         WifiNetworkSelectionConfig mWifiNetworkSelectionConfig = new WifiNetworkSelectionConfig();
257 
Builder()258         public Builder() {
259             mWifiNetworkSelectionConfig.mSufficiencyCheckEnabledWhenScreenOff = true;
260             mWifiNetworkSelectionConfig.mSufficiencyCheckEnabledWhenScreenOn = true;
261             mWifiNetworkSelectionConfig.mUserConnectChoiceOverrideEnabled = true;
262             mWifiNetworkSelectionConfig.mLastSelectionWeightEnabled = true;
263             mWifiNetworkSelectionConfig.mAssociatedNetworkSelectionOverride =
264                     ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE;
265             mWifiNetworkSelectionConfig.mRssi2Thresholds = new int[4];
266             mWifiNetworkSelectionConfig.mRssi5Thresholds = new int[4];
267             mWifiNetworkSelectionConfig.mRssi6Thresholds = new int[4];
268             mWifiNetworkSelectionConfig.mFrequencyWeights = new SparseArray<>();
269         }
270 
Builder(@onNull WifiNetworkSelectionConfig config)271         public Builder(@NonNull WifiNetworkSelectionConfig config) {
272             mWifiNetworkSelectionConfig = config;
273         }
274 
275         /**
276          * This setting affects wifi network selection behavior while already connected to a
277          * network, and is only relevant if associated network selection
278          * (see {@link #setAssociatedNetworkSelectionOverride(int)}) is enabled. Enable or disable
279          * network sufficiency check when wifi is connected and the screen is off.
280          * <p>
281          * If the sufficiency check is enabled, multiple parameters such as the RSSI and estimated
282          * throughput will be used to determine if the current network is sufficient. When the
283          * current network is found sufficient, the wifi framework will not attempt a network switch
284          * even if a potentially better network is available. When the current network is found
285          * insufficient, the wifi framework will keep trying to score other networks against the
286          * current network attempting to find and connect to a better alternative.
287          * <p>
288          * If the sufficiency check is disabled, then the currently connected network will always
289          * be considered insufficient. See the previous paragraph on the wifi framework's behavior
290          * when the current network is insufficient.
291          * <p>
292          * By default, network sufficiency check is enabled for both screen on and screen off cases.
293          * @param enabled Set to true to enable sufficiency check, and false to disable sufficiency
294          *                check.
295          */
setSufficiencyCheckEnabledWhenScreenOff(boolean enabled)296         public @NonNull Builder setSufficiencyCheckEnabledWhenScreenOff(boolean enabled) {
297             mWifiNetworkSelectionConfig.mSufficiencyCheckEnabledWhenScreenOff = enabled;
298             return this;
299         }
300 
301         /**
302          * This setting affects wifi network selection behavior while already connected to a
303          * network, and is only relevant if associated network selection
304          * (see {@link #setAssociatedNetworkSelectionOverride(int)}) is enabled. Enable or disable
305          * network sufficiency check when wifi is connected and the screen is on.
306          * <p>
307          * If the sufficiency check is enabled, multiple parameters such as the RSSI and estimated
308          * throughput will be used to determine if the current network is sufficient. When the
309          * current network is found sufficient, the wifi framework will not attempt a network switch
310          * even if a potentially better network is available. When the current network is found
311          * insufficient, the wifi framework will keep trying to score other networks against the
312          * current network attempting to find and connect to a better alternative.
313          * <p>
314          * If the sufficiency check is disabled, then the currently connected network will always
315          * be considered insufficient. See the previous paragraph on the wifi framework's behavior
316          * when the current network is insufficient.
317          * <p>
318          * By default, network sufficiency check is enabled for both screen on and screen off cases.
319          * @param enabled Set to true to enable sufficiency check, and false to disable sufficiency
320          *                check.
321          */
setSufficiencyCheckEnabledWhenScreenOn(boolean enabled)322         public @NonNull Builder setSufficiencyCheckEnabledWhenScreenOn(boolean enabled) {
323             mWifiNetworkSelectionConfig.mSufficiencyCheckEnabledWhenScreenOn = enabled;
324             return this;
325         }
326 
327         /**
328          * Override the value programmed by the
329          * {@code config_wifi_framework_enable_associated_network_selection} overlay with one of the
330          * {@code ASSOCIATED_NETWORK_SELECTION_OVERRIDE_} values. When the overlay is enabled,
331          * the wifi framework is allowed to automatically select and switch to a better wifi
332          * network while already connected. When the overlay is disabled, the wifi framework will
333          * simply stay connected to the connected network and will not attempt to automatically
334          * switch to another network.
335          * <p>
336          * By default, there is no override, and the framework will use the value set in the
337          * overlay.
338          * @param override the value to override the overlay as.
339          * @throws IllegalArgumentException if the input is invalid.
340          */
setAssociatedNetworkSelectionOverride( @ssociatedNetworkSelectionOverride int override)341         public @NonNull Builder setAssociatedNetworkSelectionOverride(
342                 @AssociatedNetworkSelectionOverride int override) throws IllegalArgumentException {
343             if (!isValidAssociatedNetworkSelectionOverride(override)) {
344                 throw new IllegalArgumentException("Invalid override=" + override);
345             }
346             mWifiNetworkSelectionConfig.mAssociatedNetworkSelectionOverride = override;
347             return this;
348         }
349 
350         /**
351          * Enable or disable candidate override with the user connect choice.
352          * <p>
353          * If the override is enabled, the network selector overrides any selected candidate
354          * with a network previously chosen by the user over the candidate (i.e. when the
355          * candidate was connected the user explicitly selected another network), if one exists.
356          * <p>
357          * If the override is disabled, network selector uses the network nominator candidate
358          * and does not override it with the user chosen configuration.
359          * <p>
360          * By default, user connect choice override is enabled.
361          * @param enabled Set to true to enable candidate override with the user connect choice,
362          *                and false to disable the override.
363          */
setUserConnectChoiceOverrideEnabled(boolean enabled)364         public @NonNull Builder setUserConnectChoiceOverrideEnabled(boolean enabled) {
365             mWifiNetworkSelectionConfig.mUserConnectChoiceOverrideEnabled = enabled;
366             return this;
367         }
368 
369         /**
370          * Enable or disable last selection weight.
371          * <p>
372          * If the last selection weight is enabled, network selector prefers the latest
373          * user selected network over all other networks for a limited duration.
374          * This duration is configurable via {@code config_wifiFrameworkLastSelectionMinutes}.
375          * <p>
376          * If the last selection weight is disabled, network selector does not prefer a
377          * recently selected network over other networks.
378          * <p>
379          * By default, last selection weight is enabled.
380          * @param enabled Set to true to enable the last selection weight,
381          *                and false to disable it.
382          */
setLastSelectionWeightEnabled(boolean enabled)383         public @NonNull Builder setLastSelectionWeightEnabled(boolean enabled) {
384             mWifiNetworkSelectionConfig.mLastSelectionWeightEnabled = enabled;
385             return this;
386         }
387 
388         /**
389          * Sets the RSSI thresholds for the input band.
390          * <p>
391          * If the RSSI thresholds are set, network selector uses these values over the
392          * following overlay configured values for the specified input band.
393          * For {@code ScanResult.WIFI_BAND_24_GHZ}:
394          * <ul>
395          *     <li>{@code config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz}</li>
396          *     <li>{@code config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz}</li>
397          *     <li>{@code config_wifi_framework_wifi_score_low_rssi_threshold_24GHz}</li>
398          *     <li>{@code config_wifi_framework_wifi_score_good_rssi_threshold_24GHz}</li>
399          * </ul>
400          * For {@code ScanResult.WIFI_BAND_5_GHZ}:
401          * <ul>
402          *     <li>{@code config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz}</li>
403          *     <li>{@code config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz}</li>
404          *     <li>{@code config_wifi_framework_wifi_score_low_rssi_threshold_5GHz}</li>
405          *     <li>{@code config_wifi_framework_wifi_score_good_rssi_threshold_5GHz}</li>
406          * </ul>
407          * For {@code ScanResult.WIFI_BAND_6_GHZ}:
408          * <ul>
409          *     <li>{@code config_wifiFrameworkScoreBadRssiThreshold6ghz}</li>
410          *     <li>{@code config_wifiFrameworkScoreEntryRssiThreshold6ghz}</li>
411          *     <li>{@code config_wifiFrameworkScoreLowRssiThreshold6ghz}</li>
412          *     <li>{@code config_wifiFrameworkScoreGoodRssiThreshold6ghz}</li>
413          * </ul>
414          * <p>
415          * The input thresholds override the overlays listed above in the respective order
416          * so it must be an int array with 4 values.
417          * The values must be between -126 and -1 and the array must be strictly increasing.
418          * For example, [-80, -70, -60, -50] is a valid input while [-70, -70, -60, -50] is not
419          * since the array is not strictly increasing.
420          * The only exception to these rules is [0, 0, 0, 0], which is used to remove any
421          * RSSI thresholds set.
422          * <p>
423          * The input band must be one of the following {@link WifiBand}:
424          * <ul>
425          *     <li>{@code ScanResult.WIFI_BAND_24_GHZ}</li>
426          *     <li>{@code ScanResult.WIFI_BAND_5_GHZ}</li>
427          *     <li>{@code ScanResult.WIFI_BAND_6_GHZ}</li>
428          * </ul>
429          * <p>
430          * To remove the RSSI thresholds set, pass in an array with 0s as the thresholds.
431          * The network selector will go back to using the overlay configured values.
432          * @param band {@link WifiBand} you want to set the RSSI thresholds for
433          * @param thresholds RSSI thresholds
434          * @throws IllegalArgumentException if the input is invalid.
435          */
setRssiThresholds(@ifiBand int band, @NonNull int[] thresholds)436         public @NonNull Builder setRssiThresholds(@WifiBand int band, @NonNull int[] thresholds)
437                 throws IllegalArgumentException {
438             if (!isValidRssiThresholdArray(thresholds)) {
439                 throw new IllegalArgumentException("Invalid RSSI thresholds="
440                         + Arrays.toString(thresholds));
441             }
442             if (!isValidBand(band)) {
443                 throw new IllegalArgumentException("Invalid band=" + band);
444             }
445             switch (band) {
446                 case ScanResult.WIFI_BAND_24_GHZ:
447                     mWifiNetworkSelectionConfig.mRssi2Thresholds = thresholds;
448                     break;
449                 case ScanResult.WIFI_BAND_5_GHZ:
450                     mWifiNetworkSelectionConfig.mRssi5Thresholds = thresholds;
451                     break;
452                 case ScanResult.WIFI_BAND_6_GHZ:
453                     mWifiNetworkSelectionConfig.mRssi6Thresholds = thresholds;
454                     break;
455             }
456             return this;
457         }
458 
459         /**
460          * Sets the frequency weights that will be used by the network selector to provide
461          * a bonus or penalty to the specified frequencies in the list.
462          * <p>
463          * The input SparseArray has to adhere to the following (key, value) format.
464          * Key: frequency the weight needs to be applied to in MHz (ex. 5201MHz -> 5201)
465          * Value: one of {@link FrequencyWeight}
466          * <ul>
467          *      <li>{@link #FREQUENCY_WEIGHT_LOW}</li>
468          *      <li>{@link #FREQUENCY_WEIGHT_HIGH}</li>
469          * </ul>
470          * <p>
471          * By default, all frequencies not present in the list will not have any frequency weight.
472          * <p>
473          * To removed the frequency weights set, pass in an empty SparseArray.
474          * The network selector will go back to treating all the frequencies with
475          * an equal preference.
476          * @param weights frequency weights
477          * @throws IllegalArgumentException if the input is invalid.
478          */
setFrequencyWeights(@onNull SparseArray<Integer> weights)479         public @NonNull Builder setFrequencyWeights(@NonNull SparseArray<Integer> weights)
480                 throws IllegalArgumentException {
481             if (!isValidFrequencyWeightArray(weights)) {
482                 if (weights == null) {
483                     throw new IllegalArgumentException("Invalid frequency weights=null");
484                 }
485                 throw new IllegalArgumentException("Invalid frequency weights="
486                         + weights.toString());
487             }
488             mWifiNetworkSelectionConfig.mFrequencyWeights = weights;
489             return this;
490         }
491 
492         /**
493          * Creates a WifiNetworkSelectionConfig for use in
494          * {@link WifiManager#setNetworkSelectionConfig(WifiNetworkSelectionConfig, Consumer)}
495          */
build()496         public @NonNull WifiNetworkSelectionConfig build() {
497             return new WifiNetworkSelectionConfig(mWifiNetworkSelectionConfig);
498         }
499     }
500 
501     @Override
hashCode()502     public int hashCode() {
503         return Objects.hash(mSufficiencyCheckEnabledWhenScreenOff,
504                 mSufficiencyCheckEnabledWhenScreenOn, mAssociatedNetworkSelectionOverride,
505                 mUserConnectChoiceOverrideEnabled, mLastSelectionWeightEnabled,
506                 Arrays.hashCode(mRssi2Thresholds), Arrays.hashCode(mRssi5Thresholds),
507                 Arrays.hashCode(mRssi6Thresholds), mFrequencyWeights.contentHashCode());
508     }
509 
510     @Override
equals(Object obj)511     public boolean equals(Object obj) {
512         if (this == obj) {
513             return true;
514         }
515         if (!(obj instanceof WifiNetworkSelectionConfig)) {
516             return false;
517         }
518         WifiNetworkSelectionConfig lhs = (WifiNetworkSelectionConfig) obj;
519         return mSufficiencyCheckEnabledWhenScreenOff == lhs.mSufficiencyCheckEnabledWhenScreenOff
520                 && mSufficiencyCheckEnabledWhenScreenOn == lhs.mSufficiencyCheckEnabledWhenScreenOn
521                 && mAssociatedNetworkSelectionOverride == lhs.mAssociatedNetworkSelectionOverride
522                 && mUserConnectChoiceOverrideEnabled == lhs.mUserConnectChoiceOverrideEnabled
523                 && mLastSelectionWeightEnabled == lhs.mLastSelectionWeightEnabled
524                 && Arrays.equals(mRssi2Thresholds, lhs.mRssi2Thresholds)
525                 && Arrays.equals(mRssi5Thresholds, lhs.mRssi5Thresholds)
526                 && Arrays.equals(mRssi6Thresholds, lhs.mRssi6Thresholds)
527                 && mFrequencyWeights.contentEquals(lhs.mFrequencyWeights);
528     }
529 
530     public static final @NonNull Creator<WifiNetworkSelectionConfig> CREATOR =
531             new Creator<WifiNetworkSelectionConfig>() {
532                 @Override
533                 public WifiNetworkSelectionConfig createFromParcel(Parcel in) {
534                     WifiNetworkSelectionConfig config = new WifiNetworkSelectionConfig();
535                     config.mSufficiencyCheckEnabledWhenScreenOff = in.readBoolean();
536                     config.mSufficiencyCheckEnabledWhenScreenOn = in.readBoolean();
537                     config.mAssociatedNetworkSelectionOverride = in.readInt();
538                     config.mUserConnectChoiceOverrideEnabled = in.readBoolean();
539                     config.mLastSelectionWeightEnabled = in.readBoolean();
540                     in.readIntArray(config.mRssi2Thresholds);
541                     in.readIntArray(config.mRssi5Thresholds);
542                     in.readIntArray(config.mRssi6Thresholds);
543                     config.mFrequencyWeights = in.readSparseArray(null, java.lang.Integer.class);
544                     return config;
545                 }
546 
547                 @Override
548                 public WifiNetworkSelectionConfig[] newArray(int size) {
549                     return new WifiNetworkSelectionConfig[size];
550                 }
551             };
552 
553     @Override
describeContents()554     public int describeContents() {
555         return 0;
556     }
557 
558     @Override
writeToParcel(@onNull Parcel dest, int flags)559     public void writeToParcel(@NonNull Parcel dest, int flags) {
560         dest.writeBoolean(mSufficiencyCheckEnabledWhenScreenOff);
561         dest.writeBoolean(mSufficiencyCheckEnabledWhenScreenOn);
562         dest.writeInt(mAssociatedNetworkSelectionOverride);
563         dest.writeBoolean(mUserConnectChoiceOverrideEnabled);
564         dest.writeBoolean(mLastSelectionWeightEnabled);
565         dest.writeIntArray(mRssi2Thresholds);
566         dest.writeIntArray(mRssi5Thresholds);
567         dest.writeIntArray(mRssi6Thresholds);
568         dest.writeSparseArray(mFrequencyWeights);
569     }
570 
571     @Override
toString()572     public String toString() {
573         StringBuilder sb = new StringBuilder();
574         sb.append("mSufficiencyCheckEnabledWhenScreenOff=")
575                 .append(mSufficiencyCheckEnabledWhenScreenOff)
576                 .append(", mSufficiencyCheckEnabledWhenScreenOn=")
577                 .append(mSufficiencyCheckEnabledWhenScreenOn)
578                 .append(", mAssociatedNetworkSelectionOverride=")
579                 .append(mAssociatedNetworkSelectionOverride)
580                 .append(", mUserConnectChoiceOverrideEnabled=")
581                 .append(mUserConnectChoiceOverrideEnabled)
582                 .append(", mLastSelectionWeightEnabled=")
583                 .append(mLastSelectionWeightEnabled)
584                 .append(", mRssi2Thresholds=")
585                 .append(Arrays.toString(mRssi2Thresholds))
586                 .append(", mRssi5Thresholds=")
587                 .append(Arrays.toString(mRssi5Thresholds))
588                 .append(", mRssi6Thresholds=")
589                 .append(Arrays.toString(mRssi6Thresholds))
590                 .append(", mFrequencyWeights=")
591                 .append(mFrequencyWeights.toString());
592         return sb.toString();
593     }
594 }
595