1 /*
2  * Copyright (C) 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 android.net.wifi;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import android.annotation.FlaggedApi;
22 import android.annotation.IntDef;
23 import android.annotation.IntRange;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SystemApi;
28 import android.net.MacAddress;
29 import android.net.NetworkCapabilities;
30 import android.net.NetworkRequest;
31 import android.net.wifi.hotspot2.PasspointConfiguration;
32 import android.os.Build;
33 import android.os.Parcel;
34 import android.os.ParcelUuid;
35 import android.os.Parcelable;
36 import android.telephony.SubscriptionInfo;
37 import android.telephony.SubscriptionManager;
38 import android.telephony.TelephonyManager;
39 import android.text.TextUtils;
40 
41 import androidx.annotation.RequiresApi;
42 
43 import com.android.modules.utils.build.SdkLevel;
44 import com.android.wifi.flags.Flags;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.nio.charset.CharsetEncoder;
49 import java.nio.charset.StandardCharsets;
50 import java.util.List;
51 import java.util.Objects;
52 
53 /**
54  * The Network Suggestion object is used to provide a Wi-Fi network for consideration when
55  * auto-connecting to networks. Apps cannot directly create this object, they must use
56  * {@link WifiNetworkSuggestion.Builder#build()} to obtain an instance of this object.
57  *<p>
58  * Apps can provide a list of such networks to the platform using
59  * {@link WifiManager#addNetworkSuggestions(List)}.
60  */
61 public final class WifiNetworkSuggestion implements Parcelable {
62     /** @hide */
63     @Retention(RetentionPolicy.SOURCE)
64     @IntDef(prefix = {"RANDOMIZATION_"}, value = {
65             RANDOMIZATION_PERSISTENT,
66             RANDOMIZATION_NON_PERSISTENT})
67     public @interface MacRandomizationSetting {}
68     /**
69      * Generate a randomized MAC from a secret seed and information from the Wi-Fi configuration
70      * (SSID or Passpoint profile) and reuse it for all connections to this network. The
71      * randomized MAC address for this network will stay the same for each subsequent association
72      * until the device undergoes factory reset.
73      */
74     public static final int RANDOMIZATION_PERSISTENT = 0;
75     /**
76      * With this option, the randomized MAC address will periodically get re-randomized, and
77      * the randomized MAC address will change if the suggestion is removed and then added back.
78      */
79     public static final int RANDOMIZATION_NON_PERSISTENT = 1;
80     /**
81      * Builder used to create {@link WifiNetworkSuggestion} objects.
82      */
83     public static final class Builder {
84         private static final int UNASSIGNED_PRIORITY = -1;
85 
86         /**
87          * Set WPA Enterprise type according to certificate security level.
88          * This is for backward compatibility in R.
89          */
90         private static final int WPA3_ENTERPRISE_AUTO = 0;
91         /** Set WPA Enterprise type to standard mode only. */
92         private static final int WPA3_ENTERPRISE_STANDARD = 1;
93         /** Set WPA Enterprise type to 192 bit mode only. */
94         private static final int WPA3_ENTERPRISE_192_BIT = 2;
95 
96         /**
97          * SSID of the network.
98          */
99         private WifiSsid mWifiSsid;
100         /**
101          * Optional BSSID within the network.
102          */
103         private MacAddress mBssid;
104         /**
105          * Whether this is an OWE network or not.
106          */
107         private boolean mIsEnhancedOpen;
108         /**
109          * Pre-shared key for use with WPA-PSK networks.
110          */
111         private @Nullable String mWpa2PskPassphrase;
112         /**
113          * Pre-shared key for use with WPA3-SAE networks.
114          */
115         private @Nullable String mWpa3SaePassphrase;
116         /**
117          * The enterprise configuration details specifying the EAP method,
118          * certificates and other settings associated with the WPA/WPA2-Enterprise networks.
119          */
120         private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
121         /**
122          * The enterprise configuration details specifying the EAP method,
123          * certificates and other settings associated with the WPA3-Enterprise networks.
124          */
125         private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
126         /**
127          * Indicate what type this WPA3-Enterprise network is.
128          */
129         private int mWpa3EnterpriseType = WPA3_ENTERPRISE_AUTO;
130         /**
131          * The passpoint config for use with Hotspot 2.0 network
132          */
133         private @Nullable PasspointConfiguration mPasspointConfiguration;
134         /**
135          * This is a network that does not broadcast its SSID, so an
136          * SSID-specific probe request must be used for scans.
137          */
138         private boolean mIsHiddenSSID;
139         /**
140          * Whether app needs to log in to captive portal to obtain Internet access.
141          */
142         private boolean mIsAppInteractionRequired;
143         /**
144          * Whether user needs to log in to captive portal to obtain Internet access.
145          */
146         private boolean mIsUserInteractionRequired;
147         /**
148          * Whether this network is metered or not.
149          */
150         private int mMeteredOverride;
151         /**
152          * Priority of this network among other network suggestions from same priority group
153          * provided by the app.
154          * The higher the number, the higher the priority (i.e value of 0 = lowest priority).
155          */
156         private int mPriority;
157         /**
158          * Priority group ID, while suggestion priority will only effect inside the priority group.
159          */
160         private int mPriorityGroup;
161 
162         /**
163          * The carrier ID identifies the operator who provides this network configuration.
164          *    see {@link TelephonyManager#getSimCarrierId()}
165          */
166         private int mCarrierId;
167 
168         /**
169          * The Subscription ID identifies the SIM card for which this network configuration is
170          * valid.
171          */
172         private int mSubscriptionId;
173 
174         /**
175          * Whether this network is shared credential with user to allow user manually connect.
176          */
177         private boolean mIsSharedWithUser;
178 
179         /**
180          * Whether the setCredentialSharedWithUser have been called.
181          */
182         private boolean mIsSharedWithUserSet;
183 
184         /**
185          * Whether this network is initialized with auto-join enabled (the default) or not.
186          */
187         private boolean mIsInitialAutojoinEnabled;
188 
189         /**
190          * Pre-shared key for use with WAPI-PSK networks.
191          */
192         private @Nullable String mWapiPskPassphrase;
193 
194         /**
195          * The enterprise configuration details specifying the EAP method,
196          * certificates and other settings associated with the WAPI networks.
197          */
198         private @Nullable WifiEnterpriseConfig mWapiEnterpriseConfig;
199 
200         /**
201          * Whether this network will be brought up as untrusted (TRUSTED capability bit removed).
202          */
203         private boolean mIsNetworkUntrusted;
204 
205         /**
206          * Whether this network will be brought up as OEM paid (OEM_PAID capability bit added).
207          */
208         private boolean mIsNetworkOemPaid;
209 
210         /**
211          * Whether this network will be brought up as OEM private (OEM_PRIVATE capability bit
212          * added).
213          */
214         private boolean mIsNetworkOemPrivate;
215 
216         /**
217          * Whether this network is a carrier merged network.
218          */
219         private boolean mIsCarrierMerged;
220 
221         /**
222          * The MAC randomization strategy.
223          */
224         @MacRandomizationSetting
225         private int mMacRandomizationSetting;
226 
227         /**
228          * The SAE Hash-to-Element only mode.
229          */
230         private boolean mSaeH2eOnlyMode;
231 
232         /**
233          * Whether this network will be brought up as restricted
234          */
235         private boolean mIsNetworkRestricted;
236 
237         /**
238          * Whether enable Wi-Fi 7 for this network
239          */
240         private boolean mIsWifi7Enabled;
241 
242 
243         /**
244          * The Subscription group UUID identifies the SIM cards for which this network configuration
245          * is valid.
246          */
247         private ParcelUuid mSubscriptionGroup;
248 
Builder()249         public Builder() {
250             mBssid =  null;
251             mIsEnhancedOpen = false;
252             mWpa2PskPassphrase = null;
253             mWpa3SaePassphrase = null;
254             mWpa2EnterpriseConfig = null;
255             mWpa3EnterpriseConfig = null;
256             mPasspointConfiguration = null;
257             mIsHiddenSSID = false;
258             mIsAppInteractionRequired = false;
259             mIsUserInteractionRequired = false;
260             mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NONE;
261             mIsSharedWithUser = true;
262             mIsSharedWithUserSet = false;
263             mIsInitialAutojoinEnabled = true;
264             mPriority = UNASSIGNED_PRIORITY;
265             mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
266             mWapiPskPassphrase = null;
267             mWapiEnterpriseConfig = null;
268             mIsNetworkUntrusted = false;
269             mIsNetworkOemPaid = false;
270             mIsNetworkOemPrivate = false;
271             mIsCarrierMerged = false;
272             mPriorityGroup = 0;
273             mMacRandomizationSetting = RANDOMIZATION_PERSISTENT;
274             mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
275             mSaeH2eOnlyMode = false;
276             mIsNetworkRestricted = false;
277             mSubscriptionGroup = null;
278             mWifiSsid = null;
279             mIsWifi7Enabled = true;
280         }
281 
282         /**
283          * Set the unicode SSID for the network.
284          * <p>
285          * <li>Overrides any previous value set using {@link #setSsid(String)}.</li>
286          *
287          * <p>
288          * Note: use {@link #setWifiSsid(WifiSsid)} which supports both unicode and non-unicode
289          * SSID.
290          *
291          * @param ssid The SSID of the network. It must be valid Unicode.
292          * @return Instance of {@link Builder} to enable chaining of the builder method.
293          * @throws IllegalArgumentException if the SSID is not valid unicode.
294          */
setSsid(@onNull String ssid)295         public @NonNull Builder setSsid(@NonNull String ssid) {
296             checkNotNull(ssid);
297             final CharsetEncoder unicodeEncoder = StandardCharsets.UTF_8.newEncoder();
298             if (!unicodeEncoder.canEncode(ssid)) {
299                 throw new IllegalArgumentException("SSID is not a valid unicode string");
300             }
301             mWifiSsid = WifiSsid.fromUtf8Text(ssid);
302             return this;
303         }
304 
305         /**
306          * Set the SSID for the network. {@link WifiSsid} support both unicode and non-unicode SSID.
307          * <p>
308          * <li>Overrides any previous value set using {@link #setWifiSsid(WifiSsid)}
309          * or {@link #setSsid(String)}.</li>
310          * <p>
311          * Note: this method is the superset of the {@link #setSsid(String)}
312          *
313          * @param wifiSsid The SSID of the network, in {@link WifiSsid} format.
314          * @return Instance of {@link Builder} to enable chaining of the builder method.
315          * @throws IllegalArgumentException if the wifiSsid is invalid.
316          */
setWifiSsid(@onNull WifiSsid wifiSsid)317         public @NonNull Builder setWifiSsid(@NonNull WifiSsid wifiSsid) {
318             checkNotNull(wifiSsid);
319             if (wifiSsid.getBytes().length == 0) {
320                 throw new IllegalArgumentException("Empty WifiSsid is invalid");
321             }
322             mWifiSsid = WifiSsid.fromBytes(wifiSsid.getBytes());
323             return this;
324         }
325 
326         /**
327          * Set the BSSID to use for filtering networks from scan results. Will only match network
328          * whose BSSID is identical to the specified value.
329          * <p>
330          * <li Sets a specific BSSID for the network suggestion. If set, only the specified BSSID
331          * with the specified SSID will be considered for connection.
332          * <li>If set, only the specified BSSID with the specified SSID will be considered for
333          * connection.</li>
334          * <li>If not set, all BSSIDs with the specified SSID will be considered for connection.
335          * </li>
336          * <li>Overrides any previous value set using {@link #setBssid(MacAddress)}.</li>
337          *
338          * @param bssid BSSID of the network.
339          * @return Instance of {@link Builder} to enable chaining of the builder method.
340          */
setBssid(@onNull MacAddress bssid)341         public @NonNull Builder setBssid(@NonNull MacAddress bssid) {
342             checkNotNull(bssid);
343             mBssid = MacAddress.fromBytes(bssid.toByteArray());
344             return this;
345         }
346 
347         /**
348          * Specifies whether this represents an Enhanced Open (OWE) network.
349          *
350          * @param isEnhancedOpen {@code true} to indicate that the network used enhanced open,
351          *                       {@code false} otherwise.
352          * @return Instance of {@link Builder} to enable chaining of the builder method.
353          */
setIsEnhancedOpen(boolean isEnhancedOpen)354         public @NonNull Builder setIsEnhancedOpen(boolean isEnhancedOpen) {
355             mIsEnhancedOpen = isEnhancedOpen;
356             return this;
357         }
358 
359         /**
360          * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
361          * WPA2-PSK networks.
362          *
363          * @param passphrase passphrase of the network.
364          * @return Instance of {@link Builder} to enable chaining of the builder method.
365          * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
366          */
setWpa2Passphrase(@onNull String passphrase)367         public @NonNull Builder setWpa2Passphrase(@NonNull String passphrase) {
368             checkNotNull(passphrase);
369             final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
370             if (!asciiEncoder.canEncode(passphrase)) {
371                 throw new IllegalArgumentException("passphrase not ASCII encodable");
372             }
373             mWpa2PskPassphrase = passphrase;
374             return this;
375         }
376 
377         /**
378          * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to WPA3-SAE
379          * networks.
380          *
381          * @param passphrase passphrase of the network.
382          * @return Instance of {@link Builder} to enable chaining of the builder method.
383          * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
384          */
setWpa3Passphrase(@onNull String passphrase)385         public @NonNull Builder setWpa3Passphrase(@NonNull String passphrase) {
386             checkNotNull(passphrase);
387             final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
388             if (!asciiEncoder.canEncode(passphrase)) {
389                 throw new IllegalArgumentException("passphrase not ASCII encodable");
390             }
391             mWpa3SaePassphrase = passphrase;
392             return this;
393         }
394 
395         /**
396          * Set the associated enterprise configuration for this network. Needed for authenticating
397          * to WPA2 enterprise networks. See {@link WifiEnterpriseConfig} for description.
398          *
399          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
400          * @return Instance of {@link Builder} to enable chaining of the builder method.
401          * @throws IllegalArgumentException If configuration uses server certificate but validation
402          *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
403          */
setWpa2EnterpriseConfig( @onNull WifiEnterpriseConfig enterpriseConfig)404         public @NonNull Builder setWpa2EnterpriseConfig(
405                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
406             checkNotNull(enterpriseConfig);
407             if (enterpriseConfig.isEapMethodServerCertUsed()
408                     && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
409                 throw new IllegalArgumentException("Enterprise configuration mandates server "
410                         + "certificate but validation is not enabled.");
411             }
412             mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
413             return this;
414         }
415 
416         /**
417          * Set the associated enterprise configuration for this network. Needed for authenticating
418          * to WPA3-Enterprise networks (standard and 192-bit security). See
419          * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the
420          * client and CA certificates must be provided, and must be of type of either
421          * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384
422          * (OID 1.2.840.10045.4.3.3).
423          *
424          * @deprecated use {@link #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)} or
425          * {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} to specify
426          * WPA3-Enterprise type explicitly.
427          *
428          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
429          * @return Instance of {@link Builder} to enable chaining of the builder method.
430          * @throws IllegalArgumentException If configuration uses server certificate but validation
431          *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
432          */
433         @Deprecated
setWpa3EnterpriseConfig( @onNull WifiEnterpriseConfig enterpriseConfig)434         public @NonNull Builder setWpa3EnterpriseConfig(
435                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
436             checkNotNull(enterpriseConfig);
437             if (enterpriseConfig.isEapMethodServerCertUsed()
438                     && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
439                 throw new IllegalArgumentException("Enterprise configuration mandates server "
440                         + "certificate but validation is not enabled.");
441             }
442             mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
443             return this;
444         }
445 
446         /**
447          * Set the associated enterprise configuration for this network. Needed for authenticating
448          * to WPA3-Enterprise standard networks. See {@link WifiEnterpriseConfig} for description.
449          * For WPA3-Enterprise in 192-bit security mode networks,
450          * see {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} for description.
451          *
452          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
453          * @return Instance of {@link Builder} to enable chaining of the builder method.
454          * @throws IllegalArgumentException If configuration uses server certificate but validation
455          *                                  is not enabled. See {@link WifiEnterpriseConfig#isServerCertValidationEnabled()}
456          */
setWpa3EnterpriseStandardModeConfig( @onNull WifiEnterpriseConfig enterpriseConfig)457         public @NonNull Builder setWpa3EnterpriseStandardModeConfig(
458                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
459             checkNotNull(enterpriseConfig);
460             if (enterpriseConfig.isEapMethodServerCertUsed()
461                     && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) {
462                 throw new IllegalArgumentException("Enterprise configuration mandates server "
463                         + "certificate but validation is not enabled.");
464             }
465             mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
466             mWpa3EnterpriseType = WPA3_ENTERPRISE_STANDARD;
467             return this;
468         }
469 
470         /**
471          * Set the associated enterprise configuration for this network. Needed for authenticating
472          * to WPA3-Enterprise in 192-bit security mode networks. See {@link WifiEnterpriseConfig}
473          * for description. Both the client and CA certificates must be provided,
474          * and must be of type of either sha384WithRSAEncryption with key length of 3072bit or
475          * more (OID 1.2.840.113549.1.1.12), or ecdsa-with-SHA384 with key length of 384bit or
476          * more (OID 1.2.840.10045.4.3.3).
477          *
478          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
479          * @return Instance of {@link Builder} to enable chaining of the builder method.
480          * @throws IllegalArgumentException if the EAP type or certificates do not
481          *                                  meet 192-bit mode requirements.
482          */
setWpa3Enterprise192BitModeConfig( @onNull WifiEnterpriseConfig enterpriseConfig)483         public @NonNull Builder setWpa3Enterprise192BitModeConfig(
484                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
485             checkNotNull(enterpriseConfig);
486             if (enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS) {
487                 throw new IllegalArgumentException("The 192-bit mode network type must be TLS");
488             }
489             if (!WifiEnterpriseConfig.isSuiteBCipherCert(
490                     enterpriseConfig.getClientCertificate())) {
491                 throw new IllegalArgumentException(
492                     "The client certificate does not meet 192-bit mode requirements.");
493             }
494             if (!WifiEnterpriseConfig.isSuiteBCipherCert(
495                     enterpriseConfig.getCaCertificate())) {
496                 throw new IllegalArgumentException(
497                     "The CA certificate does not meet 192-bit mode requirements.");
498             }
499 
500             mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
501             mWpa3EnterpriseType = WPA3_ENTERPRISE_192_BIT;
502             return this;
503         }
504 
505         /**
506          * Set the associated Passpoint configuration for this network. Needed for authenticating
507          * to Hotspot 2.0 networks. See {@link PasspointConfiguration} for description.
508          *
509          * @param passpointConfig Instance of {@link PasspointConfiguration}.
510          * @return Instance of {@link Builder} to enable chaining of the builder method.
511          * @throws IllegalArgumentException if passpoint configuration is invalid.
512          */
setPasspointConfig( @onNull PasspointConfiguration passpointConfig)513         public @NonNull Builder setPasspointConfig(
514                 @NonNull PasspointConfiguration passpointConfig) {
515             checkNotNull(passpointConfig);
516             if (!passpointConfig.validate()) {
517                 throw new IllegalArgumentException("Passpoint configuration is invalid");
518             }
519             mPasspointConfiguration = new PasspointConfiguration(passpointConfig);
520             return this;
521         }
522 
523         /**
524          * Set the carrier ID of the network operator. The carrier ID associates a Suggested
525          * network with a specific carrier (and therefore SIM). The carrier ID must be provided
526          * for any network which uses the SIM-based authentication: e.g. EAP-SIM, EAP-AKA,
527          * EAP-AKA', and EAP-PEAP with SIM-based phase 2 authentication.
528          * @param carrierId see {@link TelephonyManager#getSimCarrierId()}.
529          * @return Instance of {@link Builder} to enable chaining of the builder method.
530          *
531          * @hide
532          */
533         @SystemApi
534         @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING)
setCarrierId(int carrierId)535         public @NonNull Builder setCarrierId(int carrierId) {
536             mCarrierId = carrierId;
537             return this;
538         }
539 
540         /**
541          * Configure the suggestion to only be used with the SIM identified by the subscription
542          * ID specified in this method. The suggested network will only be used by that SIM and
543          * no other SIM - even from the same carrier.
544          * <p>
545          * The caller is restricted to be either of:
546          * <li>A carrier provisioning app (which holds the
547          * {@code android.Manifest.permission#NETWORK_CARRIER_PROVISIONING} permission).
548          * <li>A carrier-privileged app - which is restricted to only specify a subscription ID
549          * which belong to the same carrier which signed the app, see
550          * {@link TelephonyManager#hasCarrierPrivileges()}.
551          * <p>
552          * Specifying a subscription ID which doesn't match these restriction will cause the
553          * suggestion to be rejected with the error code
554          * {@link WifiManager#STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED}.
555          *
556          * Only one of the {@link #setSubscriptionGroup(ParcelUuid)} and
557          * {@link #setSubscriptionId(int)} should be called for a suggestion.
558          *
559          * @param subscriptionId subscription ID see {@link SubscriptionInfo#getSubscriptionId()}
560          * @return Instance of {@link Builder} to enable chaining of the builder method.
561          * @throws IllegalArgumentException if subscriptionId equals to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
562          */
563         @RequiresApi(Build.VERSION_CODES.S)
setSubscriptionId(int subscriptionId)564         public @NonNull Builder setSubscriptionId(int subscriptionId) {
565             if (!SdkLevel.isAtLeastS()) {
566                 throw new UnsupportedOperationException();
567             }
568             if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
569                 throw new IllegalArgumentException("Subscription Id is invalid");
570             }
571             mSubscriptionId = subscriptionId;
572             return this;
573         }
574 
575         /**
576          * Configure the suggestion to only be used with the SIMs that belong to the Subscription
577          * Group specified in this method. The suggested network will only be used by the SIM in
578          * this Subscription Group and no other SIMs - even from the same carrier.
579          * <p>
580          * The caller is restricted to be either of:
581          * <li>A carrier provisioning app.
582          * <li>A carrier-privileged app - which is restricted to only specify a subscription ID
583          * which belong to the same carrier which signed the app, see
584          * {@link TelephonyManager#hasCarrierPrivileges()}.
585          * <p>
586          * Specifying a subscription group which doesn't match these restriction will cause the
587          * suggestion to be rejected with the error code
588          * {@link WifiManager#STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED}.
589          *
590          * Only one of the {@link #setSubscriptionGroup(ParcelUuid)} and
591          * {@link #setSubscriptionId(int)} should be called for a suggestion.
592          *
593          * @param groupUuid Subscription group UUID see
594          * {@link SubscriptionManager#createSubscriptionGroup(List)}
595          * @return Instance of {@link Builder} to enable chaining of the builder method.
596          * @throws IllegalArgumentException if group UUID is {@code null}.
597          */
598         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setSubscriptionGroup(@onNull ParcelUuid groupUuid)599         public @NonNull Builder setSubscriptionGroup(@NonNull ParcelUuid groupUuid) {
600             if (!SdkLevel.isAtLeastT()) {
601                 throw new UnsupportedOperationException();
602             }
603             if (groupUuid == null) {
604                 throw new IllegalArgumentException("SubscriptionGroup is invalid");
605             }
606             mSubscriptionGroup = groupUuid;
607             return this;
608         }
609 
610         /**
611          * Suggested networks are considered as part of a pool of all suggested networks and other
612          * networks (e.g. saved networks) - one of which will be selected.
613          * <ul>
614          * <li> Any app can suggest any number of networks. </li>
615          * <li> Priority: only the highest priority (0 being the lowest) currently visible suggested
616          * network or networks (multiple suggested networks may have the same priority) are added to
617          * the network selection pool.</li>
618          * </ul>
619          * <p>
620          * However, this restricts a suggesting app to have a single group of networks which can be
621          * prioritized. In some circumstances multiple groups are useful: for instance, suggesting
622          * networks for 2 different SIMs - each of which may have its own priority order.
623          * <p>
624          * Priority group: creates a separate priority group. Only the highest priority, currently
625          * visible suggested network or networks, within each priority group are included in the
626          * network selection pool.
627          * <p>
628          * Specify an arbitrary integer only used as the priority group. Use with
629          * {@link #setPriority(int)}.
630          *
631          * @param priorityGroup priority group id, if not set default is 0.
632          * @return Instance of {@link Builder} to enable chaining of the builder method.
633          */
setPriorityGroup(@ntRangefrom = 0) int priorityGroup)634         public @NonNull Builder setPriorityGroup(@IntRange(from = 0) int priorityGroup) {
635             mPriorityGroup = priorityGroup;
636             return this;
637         }
638 
639         /**
640          * Set the ASCII WAPI passphrase for this network. Needed for authenticating to
641          * WAPI-PSK networks.
642          *
643          * @param passphrase passphrase of the network.
644          * @return Instance of {@link Builder} to enable chaining of the builder method.
645          * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
646          *
647          */
setWapiPassphrase(@onNull String passphrase)648         public @NonNull Builder setWapiPassphrase(@NonNull String passphrase) {
649             checkNotNull(passphrase);
650             final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
651             if (!asciiEncoder.canEncode(passphrase)) {
652                 throw new IllegalArgumentException("passphrase not ASCII encodable");
653             }
654             mWapiPskPassphrase = passphrase;
655             return this;
656         }
657 
658         /**
659          * Set the associated enterprise configuration for this network. Needed for authenticating
660          * to WAPI-CERT networks. See {@link WifiEnterpriseConfig} for description.
661          *
662          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
663          * @return Instance of {@link Builder} to enable chaining of the builder method.
664          */
setWapiEnterpriseConfig( @onNull WifiEnterpriseConfig enterpriseConfig)665         public @NonNull Builder setWapiEnterpriseConfig(
666                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
667             checkNotNull(enterpriseConfig);
668             mWapiEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
669             return this;
670         }
671 
672         /**
673          * Specifies whether this represents a hidden network.
674          * <p>
675          * <li>If not set, defaults to false (i.e not a hidden network).</li>
676          *
677          * @param isHiddenSsid {@code true} to indicate that the network is hidden, {@code false}
678          *                     otherwise.
679          * @return Instance of {@link Builder} to enable chaining of the builder method.
680          */
setIsHiddenSsid(boolean isHiddenSsid)681         public @NonNull Builder setIsHiddenSsid(boolean isHiddenSsid) {
682             mIsHiddenSSID = isHiddenSsid;
683             return this;
684         }
685 
686         /**
687          * Specifies the MAC randomization method.
688          * <p>
689          * Suggested networks will never use the device (factory) MAC address to associate to the
690          * network - instead they use a locally generated random MAC address. This method controls
691          * the strategy for generating the random MAC address. If not set, defaults to
692          * {@link #RANDOMIZATION_PERSISTENT}.
693          *
694          * @param macRandomizationSetting - one of {@code RANDOMIZATION_*} values
695          * @return Instance of {@link Builder} to enable chaining of the builder method.
696          */
setMacRandomizationSetting( @acRandomizationSetting int macRandomizationSetting)697         public @NonNull Builder setMacRandomizationSetting(
698                 @MacRandomizationSetting int macRandomizationSetting) {
699             switch (macRandomizationSetting) {
700                 case RANDOMIZATION_PERSISTENT:
701                 case RANDOMIZATION_NON_PERSISTENT:
702                     mMacRandomizationSetting = macRandomizationSetting;
703                     break;
704                 default:
705                     throw new IllegalArgumentException();
706             }
707             return this;
708         }
709 
710         /**
711          * Specifies whether the app needs to log in to a captive portal to obtain Internet access.
712          * <p>
713          * This will dictate if the directed broadcast
714          * {@link WifiManager#ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} will be sent to the
715          * app after successfully connecting to the network.
716          * Use this for captive portal type networks where the app needs to authenticate the user
717          * before the device can access the network.
718          * <p>
719          * <li>If not set, defaults to false (i.e no app interaction required).</li>
720          *
721          * @param isAppInteractionRequired {@code true} to indicate that app interaction is
722          *                                 required, {@code false} otherwise.
723          * @return Instance of {@link Builder} to enable chaining of the builder method.
724          */
setIsAppInteractionRequired(boolean isAppInteractionRequired)725         public @NonNull Builder setIsAppInteractionRequired(boolean isAppInteractionRequired) {
726             mIsAppInteractionRequired = isAppInteractionRequired;
727             return this;
728         }
729 
730         /**
731          * Specifies whether the user needs to log in to a captive portal to obtain Internet access.
732          * <p>
733          * <li>If not set, defaults to false (i.e no user interaction required).</li>
734          *
735          * @param isUserInteractionRequired {@code true} to indicate that user interaction is
736          *                                  required, {@code false} otherwise.
737          * @return Instance of {@link Builder} to enable chaining of the builder method.
738          */
setIsUserInteractionRequired(boolean isUserInteractionRequired)739         public @NonNull Builder setIsUserInteractionRequired(boolean isUserInteractionRequired) {
740             mIsUserInteractionRequired = isUserInteractionRequired;
741             return this;
742         }
743 
744         /**
745          * Specify the priority of this network among other network suggestions provided by the same
746          * app and within the same priority group, see {@link #setPriorityGroup(int)}. Priorities
747          * have no impact on suggestions by other apps or suggestions from the same app using a
748          * different priority group. The higher the number, the higher the priority
749          * (i.e value of 0 = lowest priority). If not set, defaults to a lower priority than any
750          * assigned priority.
751          *
752          * @param priority Integer number representing the priority among suggestions by the app.
753          * @return Instance of {@link Builder} to enable chaining of the builder method.
754          * @throws IllegalArgumentException if the priority value is negative.
755          */
setPriority(@ntRangefrom = 0) int priority)756         public @NonNull Builder setPriority(@IntRange(from = 0) int priority) {
757             if (priority < 0) {
758                 throw new IllegalArgumentException("Invalid priority value " + priority);
759             }
760             mPriority = priority;
761             return this;
762         }
763 
764         /**
765          * Specifies whether this network is metered.
766          * <p>
767          * <li>If not set, defaults to detect automatically.</li>
768          *
769          * @param isMetered {@code true} to indicate that the network is metered, {@code false}
770          *                  for not metered.
771          * @return Instance of {@link Builder} to enable chaining of the builder method.
772          */
setIsMetered(boolean isMetered)773         public @NonNull Builder setIsMetered(boolean isMetered) {
774             if (isMetered) {
775                 mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
776             } else {
777                 mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
778             }
779             return this;
780         }
781 
782         /**
783          * Specifies whether the network credentials provided with this suggestion can be used by
784          * the user to explicitly (manually) connect to this network. If true this network will
785          * appear in the Wi-Fi Picker (in Settings) and the user will be able to select and connect
786          * to it with the provided credentials. If false, the user will need to enter network
787          * credentials and the resulting configuration will become a user saved network.
788          * <p>
789          * <li>Note: Only valid for secure (non-open) networks.
790          * <li>If not set, defaults to true (i.e. allow user to manually connect) for secure
791          * networks and false for open networks.</li>
792          *
793          * @param isShared {@code true} to indicate that the credentials may be used by the user to
794          *                              manually connect to the network, {@code false} otherwise.
795          * @return Instance of {@link Builder} to enable chaining of the builder method.
796          */
setCredentialSharedWithUser(boolean isShared)797         public @NonNull Builder setCredentialSharedWithUser(boolean isShared) {
798             mIsSharedWithUser = isShared;
799             mIsSharedWithUserSet = true;
800             return this;
801         }
802 
803         /**
804          * Specifies whether the suggestion is created with auto-join enabled or disabled. The
805          * user may modify the auto-join configuration of a suggestion directly once the device
806          * associates to the network.
807          * <p>
808          * If auto-join is initialized as disabled the user may still be able to manually connect
809          * to the network. Therefore, disabling auto-join only makes sense if
810          * {@link #setCredentialSharedWithUser(boolean)} is set to true (the default) which
811          * itself implies a secure (non-open) network.
812          * <p>
813          * If not set, defaults to true (i.e. auto-join is initialized as enabled).
814          *
815          * @param enabled true for initializing with auto-join enabled (the default), false to
816          *                initializing with auto-join disabled.
817          * @return Instance of {@link Builder} to enable chaining of the builder method.
818          */
setIsInitialAutojoinEnabled(boolean enabled)819         public @NonNull Builder setIsInitialAutojoinEnabled(boolean enabled) {
820             mIsInitialAutojoinEnabled = enabled;
821             return this;
822         }
823 
824         /**
825          * Specifies whether the system will bring up the network (if selected) as untrusted. An
826          * untrusted network has its {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED}
827          * capability removed. The Wi-Fi network selection process may use this information to
828          * influence priority of the suggested network for Wi-Fi network selection (most likely to
829          * reduce it). The connectivity service may use this information to influence the overall
830          * network configuration of the device.
831          * <p>
832          * <li> These suggestions are only considered for network selection if a
833          * {@link NetworkRequest} without {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED}
834          * capability is filed.
835          * <li> An untrusted network's credentials may not be shared with the user using
836          * {@link #setCredentialSharedWithUser(boolean)}.</li>
837          * <li> If not set, defaults to false (i.e. network is trusted).</li>
838          *
839          * @param isUntrusted Boolean indicating whether the network should be brought up untrusted
840          *                    (if true) or trusted (if false).
841          * @return Instance of {@link Builder} to enable chaining of the builder method.
842          */
setUntrusted(boolean isUntrusted)843         public @NonNull Builder setUntrusted(boolean isUntrusted) {
844             mIsNetworkUntrusted = isUntrusted;
845             return this;
846         }
847 
848         /**
849          * Specifies whether the system will bring up the network (if selected) as restricted. A
850          * restricted network has its {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED}
851          * capability removed. The Wi-Fi network selection process may use this information to
852          * influence priority of the suggested network for Wi-Fi network selection (most likely to
853          * reduce it). The connectivity service may use this information to influence the overall
854          * network configuration of the device.
855          * <p>
856          * <li> These suggestions are only considered for network selection if a
857          * {@link NetworkRequest} without {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED}
858          * capability is filed.
859          * <li> A restricted network's credentials may not be shared with the user using
860          * {@link #setCredentialSharedWithUser(boolean)}.</li>
861          * <li> If not set, defaults to false (i.e. network is unrestricted).</li>
862          *
863          * @param isRestricted Boolean indicating whether the network should be brought up
864          *                     restricted (if true) or unrestricted (if false).
865          * @return Instance of {@link Builder} to enable chaining of the builder method.
866          */
setRestricted(boolean isRestricted)867         public @NonNull Builder setRestricted(boolean isRestricted) {
868             mIsNetworkRestricted = isRestricted;
869             return this;
870         }
871 
872         /**
873          * Sets whether Wi-Fi 7 is enabled for this network.
874          *
875          * @param enabled Enable Wi-Fi 7 if true, otherwise disable Wi-Fi 7
876          * @return Instance of {@link Builder} to enable chaining of the builder method.
877          */
878         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
setWifi7Enabled(boolean enabled)879         public @NonNull Builder setWifi7Enabled(boolean enabled) {
880             mIsWifi7Enabled = enabled;
881             return this;
882         }
883         /**
884          * Specifies whether the system will bring up the network (if selected) as OEM paid. An
885          * OEM paid network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID} capability
886          * added.
887          * Note:
888          * <li>The connectivity service may use this information to influence the overall
889          * network configuration of the device. This network is typically only available to system
890          * apps.
891          * <li>On devices which do not support concurrent connection (indicated via
892          * {@link WifiManager#isStaConcurrencyForRestrictedConnectionsSupported()}), Wi-Fi
893          * network selection process may use this information to influence priority of the
894          * suggested network for Wi-Fi network selection (most likely to reduce it).
895          * <li>On devices which support concurrent connections (indicated via
896          * {@link WifiManager#isStaConcurrencyForRestrictedConnectionsSupported()}), these
897          * OEM paid networks may be brought up as a secondary concurrent connection (primary
898          * connection will be used for networks available to the user and all apps.
899          * <p>
900          * <li> An OEM paid network's credentials may not be shared with the user using
901          * {@link #setCredentialSharedWithUser(boolean)}.</li>
902          * <li> These suggestions are only considered for network selection if a
903          * {@link NetworkRequest} with {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}
904          * capability is filed.
905          * <li> Each suggestion can have both {@link #setOemPaid(boolean)} and
906          * {@link #setOemPrivate(boolean)} set if the app wants these suggestions considered
907          * for creating either an OEM paid network or OEM private network determined based on
908          * the {@link NetworkRequest} that is active.
909          * <li> If not set, defaults to false (i.e. network is not OEM paid).</li>
910          *
911          * @param isOemPaid Boolean indicating whether the network should be brought up as OEM paid
912          *                  (if true) or not OEM paid (if false).
913          * @return Instance of {@link Builder} to enable chaining of the builder method.
914          * @hide
915          */
916         @SystemApi
917         @RequiresApi(Build.VERSION_CODES.S)
setOemPaid(boolean isOemPaid)918         public @NonNull Builder setOemPaid(boolean isOemPaid) {
919             if (!SdkLevel.isAtLeastS()) {
920                 throw new UnsupportedOperationException();
921             }
922             mIsNetworkOemPaid = isOemPaid;
923             return this;
924         }
925 
926         /**
927          * Specifies whether the system will bring up the network (if selected) as OEM private. An
928          * OEM private network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE} capability
929          * added.
930          * Note:
931          * <li>The connectivity service may use this information to influence the overall
932          * network configuration of the device. This network is typically only available to system
933          * apps.
934          * <li>On devices which do not support concurrent connection (indicated via
935          * {@link WifiManager#isStaConcurrencyForRestrictedConnectionsSupported()}), Wi-Fi
936          * network selection process may use this information to influence priority of the suggested
937          * network for Wi-Fi network selection (most likely to reduce it).
938          * <li>On devices which support concurrent connections (indicated via
939          * {@link WifiManager#isStaConcurrencyForRestrictedConnectionsSupported()}), these OEM
940          * private networks may be brought up as a secondary concurrent connection (primary
941          * connection will be used for networks available to the user and all apps.
942          * <p>
943          * <li> An OEM private network's credentials may not be shared with the user using
944          * {@link #setCredentialSharedWithUser(boolean)}.</li>
945          * <li> These suggestions are only considered for network selection if a
946          * {@link NetworkRequest} with {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}
947          * capability is filed.
948          * <li> Each suggestion can have both {@link #setOemPaid(boolean)} and
949          * {@link #setOemPrivate(boolean)} set if the app wants these suggestions considered
950          * for creating either an OEM paid network or OEM private network determined based on
951          * the {@link NetworkRequest} that is active.
952          * <li> If not set, defaults to false (i.e. network is not OEM private).</li>
953          *
954          * @param isOemPrivate Boolean indicating whether the network should be brought up as OEM
955          *                     private (if true) or not OEM private (if false).
956          * @return Instance of {@link Builder} to enable chaining of the builder method.
957          * @hide
958          */
959         @SystemApi
960         @RequiresApi(Build.VERSION_CODES.S)
setOemPrivate(boolean isOemPrivate)961         public @NonNull Builder setOemPrivate(boolean isOemPrivate) {
962             if (!SdkLevel.isAtLeastS()) {
963                 throw new UnsupportedOperationException();
964             }
965             mIsNetworkOemPrivate = isOemPrivate;
966             return this;
967         }
968 
969         /**
970          * Specifies whether the suggestion represents a carrier merged network. A carrier merged
971          * Wi-Fi network is treated as part of the mobile carrier network. Such configuration may
972          * impact the user interface and data usage accounting.
973          * <p>
974          * Only carriers with the
975          * {@link android.telephony.CarrierConfigManager#KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL}
976          * flag set to {@code true} may use this API.
977          * <p>
978          * <li>A suggestion marked as carrier merged must be metered enterprise network with a valid
979          * subscription Id set.
980          * @see #setIsMetered(boolean)
981          * @see #setSubscriptionId(int)
982          * @see #setWpa2EnterpriseConfig(WifiEnterpriseConfig)
983          * @see #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)
984          * @see #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)
985          * @see #setPasspointConfig(PasspointConfiguration)
986          * </li>
987          * <li>If not set, defaults to false (i.e. not a carrier merged network.)</li>
988          * </p>
989          * @param isCarrierMerged Boolean indicating whether the network is treated a carrier
990          *                               merged network (if true) or non-merged network (if false);
991          * @return Instance of {@link Builder} to enable chaining of the builder method.
992          */
993         @RequiresApi(Build.VERSION_CODES.S)
setCarrierMerged(boolean isCarrierMerged)994         public @NonNull Builder setCarrierMerged(boolean isCarrierMerged) {
995             if (!SdkLevel.isAtLeastS()) {
996                 throw new UnsupportedOperationException();
997             }
998             mIsCarrierMerged = isCarrierMerged;
999             return this;
1000         }
1001 
1002         /**
1003          * Specifies whether the suggestion represents an SAE network which only
1004          * accepts Hash-to-Element mode.
1005          * If this is enabled, Hunting & Pecking mode is disabled and only Hash-to-Element
1006          * mode is used for this network.
1007          * This is only valid for an SAE network which is configured using the
1008          * {@link #setWpa3Passphrase}.
1009          * Before calling this API, the application should check Hash-to-Element support using
1010          * {@link WifiManager#isWpa3SaeH2eSupported()}.
1011          *
1012          * @param enable Boolean indicating whether the network only accepts Hash-to-Element mode,
1013          *        default is false.
1014          * @return Instance of {@link Builder} to enable chaining of the builder method.
1015          */
1016         @RequiresApi(Build.VERSION_CODES.S)
setIsWpa3SaeH2eOnlyModeEnabled(boolean enable)1017         public @NonNull Builder setIsWpa3SaeH2eOnlyModeEnabled(boolean enable) {
1018             if (!SdkLevel.isAtLeastS()) {
1019                 throw new UnsupportedOperationException();
1020             }
1021             mSaeH2eOnlyMode = enable;
1022             return this;
1023         }
1024 
setSecurityParamsInWifiConfiguration( @onNull WifiConfiguration configuration)1025         private void setSecurityParamsInWifiConfiguration(
1026                 @NonNull WifiConfiguration configuration) {
1027             if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
1028                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
1029                 // WifiConfiguration.preSharedKey needs quotes around ASCII password.
1030                 configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
1031             } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
1032                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
1033                 // WifiConfiguration.preSharedKey needs quotes around ASCII password.
1034                 configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
1035                 if (mSaeH2eOnlyMode) configuration.enableSaeH2eOnlyMode(mSaeH2eOnlyMode);
1036             } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
1037                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
1038                 configuration.enterpriseConfig = mWpa2EnterpriseConfig;
1039             } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise
1040                 if (mWpa3EnterpriseType == WPA3_ENTERPRISE_AUTO
1041                         && mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
1042                         && WifiEnterpriseConfig.isSuiteBCipherCert(
1043                         mWpa3EnterpriseConfig.getClientCertificate())
1044                         && WifiEnterpriseConfig.isSuiteBCipherCert(
1045                         mWpa3EnterpriseConfig.getCaCertificate())) {
1046                     // WPA3-Enterprise in 192-bit security mode
1047                     configuration.setSecurityParams(
1048                             WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT);
1049                 } else if (mWpa3EnterpriseType == WPA3_ENTERPRISE_192_BIT) {
1050                     // WPA3-Enterprise in 192-bit security mode
1051                     configuration.setSecurityParams(
1052                             WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT);
1053                 } else {
1054                     // WPA3-Enterprise
1055                     configuration.setSecurityParams(
1056                             WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
1057                 }
1058                 configuration.enterpriseConfig = mWpa3EnterpriseConfig;
1059             } else if (mIsEnhancedOpen) { // OWE network
1060                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
1061             } else if (!TextUtils.isEmpty(mWapiPskPassphrase)) { // WAPI-PSK network.
1062                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WAPI_PSK);
1063                 // WifiConfiguration.preSharedKey needs quotes around ASCII password.
1064                 configuration.preSharedKey = "\"" + mWapiPskPassphrase + "\"";
1065             } else if (mWapiEnterpriseConfig != null) { // WAPI-CERT network
1066                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WAPI_CERT);
1067                 configuration.enterpriseConfig = mWapiEnterpriseConfig;
1068             } else { // Open network
1069                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
1070             }
1071         }
1072 
1073         /**
1074          * Helper method to build WifiConfiguration object from the builder.
1075          * @return Instance of {@link WifiConfiguration}.
1076          */
buildWifiConfiguration()1077         private WifiConfiguration buildWifiConfiguration() {
1078             final WifiConfiguration wifiConfiguration = new WifiConfiguration();
1079             // WifiConfiguration.SSID needs quotes around unicode SSID.
1080             wifiConfiguration.SSID = mWifiSsid.toString();
1081             if (mBssid != null) {
1082                 wifiConfiguration.BSSID = mBssid.toString();
1083             }
1084 
1085             setSecurityParamsInWifiConfiguration(wifiConfiguration);
1086 
1087             wifiConfiguration.hiddenSSID = mIsHiddenSSID;
1088             wifiConfiguration.priority = mPriority;
1089             wifiConfiguration.meteredOverride = mMeteredOverride;
1090             wifiConfiguration.carrierId = mCarrierId;
1091             wifiConfiguration.trusted = !mIsNetworkUntrusted;
1092             wifiConfiguration.oemPaid = mIsNetworkOemPaid;
1093             wifiConfiguration.oemPrivate = mIsNetworkOemPrivate;
1094             wifiConfiguration.carrierMerged = mIsCarrierMerged;
1095             wifiConfiguration.macRandomizationSetting =
1096                     mMacRandomizationSetting == RANDOMIZATION_NON_PERSISTENT
1097                     ? WifiConfiguration.RANDOMIZATION_NON_PERSISTENT
1098                     : WifiConfiguration.RANDOMIZATION_PERSISTENT;
1099             wifiConfiguration.subscriptionId = mSubscriptionId;
1100             wifiConfiguration.restricted = mIsNetworkRestricted;
1101             wifiConfiguration.setSubscriptionGroup(mSubscriptionGroup);
1102             wifiConfiguration.setWifi7Enabled(mIsWifi7Enabled);
1103             return wifiConfiguration;
1104         }
1105 
validateSecurityParams()1106         private void validateSecurityParams() {
1107             int numSecurityTypes = 0;
1108             numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
1109             numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
1110             numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
1111             numSecurityTypes += !TextUtils.isEmpty(mWapiPskPassphrase) ? 1 : 0;
1112             numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
1113             numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
1114             numSecurityTypes += mWapiEnterpriseConfig != null ? 1 : 0;
1115             numSecurityTypes += mPasspointConfiguration != null ? 1 : 0;
1116             if (numSecurityTypes > 1) {
1117                 throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
1118                         + " setWpa3Passphrase, setWpa2EnterpriseConfig, setWpa3EnterpriseConfig"
1119                         + " setWapiPassphrase, setWapiCertSuite, setIsWapiCertSuiteAuto"
1120                         + " or setPasspointConfig can be invoked for network suggestion");
1121             }
1122         }
1123 
buildWifiConfigurationForPasspoint()1124         private WifiConfiguration buildWifiConfigurationForPasspoint() {
1125             WifiConfiguration wifiConfiguration = new WifiConfiguration();
1126             wifiConfiguration.FQDN = mPasspointConfiguration.getHomeSp().getFqdn();
1127             wifiConfiguration.setPasspointUniqueId(mPasspointConfiguration.getUniqueId());
1128             wifiConfiguration.priority = mPriority;
1129             wifiConfiguration.meteredOverride = mMeteredOverride;
1130             wifiConfiguration.trusted = !mIsNetworkUntrusted;
1131             wifiConfiguration.oemPaid = mIsNetworkOemPaid;
1132             wifiConfiguration.oemPrivate = mIsNetworkOemPrivate;
1133             wifiConfiguration.carrierMerged = mIsCarrierMerged;
1134             wifiConfiguration.carrierId = mCarrierId;
1135             wifiConfiguration.subscriptionId = mSubscriptionId;
1136             wifiConfiguration.macRandomizationSetting =
1137                     mMacRandomizationSetting == RANDOMIZATION_NON_PERSISTENT
1138                             ? WifiConfiguration.RANDOMIZATION_NON_PERSISTENT
1139                             : WifiConfiguration.RANDOMIZATION_PERSISTENT;
1140             wifiConfiguration.restricted = mIsNetworkRestricted;
1141             wifiConfiguration.setSubscriptionGroup(mSubscriptionGroup);
1142             wifiConfiguration.setWifi7Enabled(mIsWifi7Enabled);
1143             mPasspointConfiguration.setCarrierId(mCarrierId);
1144             mPasspointConfiguration.setSubscriptionId(mSubscriptionId);
1145             mPasspointConfiguration.setSubscriptionGroup(mSubscriptionGroup);
1146             mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride);
1147             mPasspointConfiguration.setOemPrivate(mIsNetworkOemPrivate);
1148             mPasspointConfiguration.setOemPaid(mIsNetworkOemPaid);
1149             mPasspointConfiguration.setCarrierMerged(mIsCarrierMerged);
1150             // MAC randomization should always be enabled for passpoint suggestions regardless of
1151             // the PasspointConfiguration's original setting.
1152             mPasspointConfiguration.setMacRandomizationEnabled(true);
1153             mPasspointConfiguration.setNonPersistentMacRandomizationEnabled(
1154                     mMacRandomizationSetting == RANDOMIZATION_NON_PERSISTENT);
1155             return wifiConfiguration;
1156         }
1157 
1158         /**
1159          * Create a network suggestion object for use in
1160          * {@link WifiManager#addNetworkSuggestions(List)}.
1161          *
1162          *<p class="note">
1163          * <b>Note:</b> Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
1164          * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to
1165          * the platform.
1166          * </p>
1167          *
1168          * For example:
1169          * To provide credentials for one open, one WPA2, one WPA3 network with their
1170          * corresponding SSID's and one with Passpoint config:
1171          *
1172          * <pre>{@code
1173          * final WifiNetworkSuggestion suggestion1 =
1174          *      new Builder()
1175          *      .setSsid("test111111")
1176          *      .build();
1177          * final WifiNetworkSuggestion suggestion2 =
1178          *      new Builder()
1179          *      .setSsid("test222222")
1180          *      .setWpa2Passphrase("test123456")
1181          *      .build();
1182          * final WifiNetworkSuggestion suggestion3 =
1183          *      new Builder()
1184          *      .setSsid("test333333")
1185          *      .setWpa3Passphrase("test6789")
1186          *      .build();
1187          * final PasspointConfiguration passpointConfig= new PasspointConfiguration();
1188          * // configure passpointConfig to include a valid Passpoint configuration
1189          * final WifiNetworkSuggestion suggestion4 =
1190          *      new Builder()
1191          *      .setPasspointConfig(passpointConfig)
1192          *      .build();
1193          * final List<WifiNetworkSuggestion> suggestionsList =
1194          *      new ArrayList<WifiNetworkSuggestion> { {
1195          *          add(suggestion1);
1196          *          add(suggestion2);
1197          *          add(suggestion3);
1198          *          add(suggestion4);
1199          *      } };
1200          * final WifiManager wifiManager =
1201          *      context.getSystemService(Context.WIFI_SERVICE);
1202          * wifiManager.addNetworkSuggestions(suggestionsList);
1203          * // ...
1204          * }</pre>
1205          *
1206          * @return Instance of {@link WifiNetworkSuggestion}
1207          * @throws IllegalStateException on invalid params set
1208          * @see WifiNetworkSuggestion
1209          */
build()1210         public @NonNull WifiNetworkSuggestion build() {
1211             validateSecurityParams();
1212             WifiConfiguration wifiConfiguration;
1213             if (mPasspointConfiguration != null) {
1214                 if (mWifiSsid != null) {
1215                     throw new IllegalStateException("setSsid should not be invoked for suggestion "
1216                             + "with Passpoint configuration");
1217                 }
1218                 if (mIsHiddenSSID) {
1219                     throw new IllegalStateException("setIsHiddenSsid should not be invoked for "
1220                             + "suggestion with Passpoint configuration");
1221                 }
1222                 wifiConfiguration = buildWifiConfigurationForPasspoint();
1223             } else {
1224                 if (mWifiSsid == null) {
1225                     throw new IllegalStateException("setSsid should be invoked for suggestion");
1226                 }
1227                 if (mWifiSsid.getBytes().length == 0) {
1228                     throw new IllegalStateException("invalid ssid for suggestion");
1229                 }
1230                 if (mBssid != null
1231                         && (mBssid.equals(MacAddress.BROADCAST_ADDRESS)
1232                         || mBssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS))) {
1233                     throw new IllegalStateException("invalid bssid for suggestion");
1234                 }
1235                 if (TextUtils.isEmpty(mWpa3SaePassphrase) && mSaeH2eOnlyMode) {
1236                     throw new IllegalStateException(
1237                             "Hash-to-Element only mode is only allowed for the SAE network");
1238                 }
1239 
1240                 wifiConfiguration = buildWifiConfiguration();
1241                 if (wifiConfiguration.isOpenNetwork()) {
1242                     if (mIsSharedWithUserSet && mIsSharedWithUser) {
1243                         throw new IllegalStateException("Open network should not be "
1244                                 + "setCredentialSharedWithUser to true");
1245                     }
1246                     mIsSharedWithUser = false;
1247                 }
1248             }
1249             if (!mIsSharedWithUser && !mIsInitialAutojoinEnabled) {
1250                 throw new IllegalStateException("Should have not a network with both "
1251                         + "setCredentialSharedWithUser and "
1252                         + "setIsAutojoinEnabled set to false");
1253             }
1254             if (mIsNetworkUntrusted || mIsNetworkRestricted) {
1255                 if (mIsSharedWithUserSet && mIsSharedWithUser) {
1256                     throw new IllegalStateException("Should not be both"
1257                             + "setCredentialSharedWithUser and +"
1258                             + "setUntrusted or setRestricted to true");
1259                 }
1260                 mIsSharedWithUser = false;
1261             }
1262             if (mIsNetworkOemPaid) {
1263                 if (mIsSharedWithUserSet && mIsSharedWithUser) {
1264                     throw new IllegalStateException("Should not be both"
1265                             + "setCredentialSharedWithUser and +"
1266                             + "setOemPaid to true");
1267                 }
1268                 mIsSharedWithUser = false;
1269             }
1270             if (mIsNetworkOemPrivate) {
1271                 if (mIsSharedWithUserSet && mIsSharedWithUser) {
1272                     throw new IllegalStateException("Should not be both"
1273                             + "setCredentialSharedWithUser and +"
1274                             + "setOemPrivate to true");
1275                 }
1276                 mIsSharedWithUser = false;
1277             }
1278             if (mIsCarrierMerged) {
1279                 if ((mSubscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1280                         && mSubscriptionGroup == null)
1281                         || mMeteredOverride != WifiConfiguration.METERED_OVERRIDE_METERED
1282                         || !isEnterpriseSuggestion()) {
1283                     throw new IllegalStateException("A carrier merged network must be a metered, "
1284                             + "enterprise network with valid subscription Id");
1285                 }
1286             }
1287             if (mSubscriptionGroup != null
1288                     && mSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1289                 throw new IllegalStateException("Should not be set both SubscriptionGroup and "
1290                         + "SubscriptionId");
1291             }
1292             return new WifiNetworkSuggestion(
1293                     wifiConfiguration,
1294                     mPasspointConfiguration,
1295                     mIsAppInteractionRequired,
1296                     mIsUserInteractionRequired,
1297                     mIsSharedWithUser,
1298                     mIsInitialAutojoinEnabled,
1299                     mPriorityGroup);
1300         }
1301 
isEnterpriseSuggestion()1302         private boolean isEnterpriseSuggestion() {
1303             return !(mWpa2EnterpriseConfig == null && mWpa3EnterpriseConfig == null
1304                     && mWapiEnterpriseConfig == null && mPasspointConfiguration == null);
1305         }
1306     }
1307 
1308 
1309 
1310     /**
1311      * Network configuration for the provided network.
1312      * @hide
1313      */
1314     @NonNull
1315     public final WifiConfiguration wifiConfiguration;
1316 
1317     /**
1318      * Passpoint configuration for the provided network.
1319      * @hide
1320      */
1321     @Nullable
1322     public final PasspointConfiguration passpointConfiguration;
1323 
1324     /**
1325      * Whether app needs to log in to captive portal to obtain Internet access.
1326      * @hide
1327      */
1328     public final boolean isAppInteractionRequired;
1329 
1330     /**
1331      * Whether user needs to log in to captive portal to obtain Internet access.
1332      * @hide
1333      */
1334     public final boolean isUserInteractionRequired;
1335 
1336     /**
1337      * Whether app share credential with the user, allow user use provided credential to
1338      * connect network manually.
1339      * @hide
1340      */
1341     public final boolean isUserAllowedToManuallyConnect;
1342 
1343     /**
1344      * Whether the suggestion will be initialized as auto-joined or not.
1345      * @hide
1346      */
1347     public final boolean isInitialAutoJoinEnabled;
1348 
1349     /**
1350      * Priority group ID.
1351      * @hide
1352      */
1353     public final int priorityGroup;
1354 
1355     /** @hide */
WifiNetworkSuggestion()1356     public WifiNetworkSuggestion() {
1357         this.wifiConfiguration = new WifiConfiguration();
1358         this.passpointConfiguration = null;
1359         this.isAppInteractionRequired = false;
1360         this.isUserInteractionRequired = false;
1361         this.isUserAllowedToManuallyConnect = true;
1362         this.isInitialAutoJoinEnabled = true;
1363         this.priorityGroup = 0;
1364     }
1365 
1366     /** @hide */
WifiNetworkSuggestion(@onNull WifiConfiguration networkConfiguration, @Nullable PasspointConfiguration passpointConfiguration, boolean isAppInteractionRequired, boolean isUserInteractionRequired, boolean isUserAllowedToManuallyConnect, boolean isInitialAutoJoinEnabled, int priorityGroup)1367     public WifiNetworkSuggestion(@NonNull WifiConfiguration networkConfiguration,
1368                                  @Nullable PasspointConfiguration passpointConfiguration,
1369                                  boolean isAppInteractionRequired,
1370                                  boolean isUserInteractionRequired,
1371                                  boolean isUserAllowedToManuallyConnect,
1372                                  boolean isInitialAutoJoinEnabled, int priorityGroup) {
1373         checkNotNull(networkConfiguration);
1374         this.wifiConfiguration = networkConfiguration;
1375         this.passpointConfiguration = passpointConfiguration;
1376 
1377         this.isAppInteractionRequired = isAppInteractionRequired;
1378         this.isUserInteractionRequired = isUserInteractionRequired;
1379         this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
1380         this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
1381         this.priorityGroup = priorityGroup;
1382     }
1383 
1384     public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
1385             new Creator<WifiNetworkSuggestion>() {
1386                 @Override
1387                 public WifiNetworkSuggestion createFromParcel(Parcel in) {
1388                     return new WifiNetworkSuggestion(
1389                             in.readParcelable(null), // wifiConfiguration
1390                             in.readParcelable(null), // PasspointConfiguration
1391                             in.readBoolean(), // isAppInteractionRequired
1392                             in.readBoolean(), // isUserInteractionRequired
1393                             in.readBoolean(), // isSharedCredentialWithUser
1394                             in.readBoolean(),  // isAutojoinEnabled
1395                             in.readInt() // priorityGroup
1396                     );
1397                 }
1398 
1399                 @Override
1400                 public WifiNetworkSuggestion[] newArray(int size) {
1401                     return new WifiNetworkSuggestion[size];
1402                 }
1403             };
1404 
1405     @Override
describeContents()1406     public int describeContents() {
1407         return 0;
1408     }
1409 
1410     @Override
writeToParcel(Parcel dest, int flags)1411     public void writeToParcel(Parcel dest, int flags) {
1412         dest.writeParcelable(wifiConfiguration, flags);
1413         dest.writeParcelable(passpointConfiguration, flags);
1414         dest.writeBoolean(isAppInteractionRequired);
1415         dest.writeBoolean(isUserInteractionRequired);
1416         dest.writeBoolean(isUserAllowedToManuallyConnect);
1417         dest.writeBoolean(isInitialAutoJoinEnabled);
1418         dest.writeInt(priorityGroup);
1419     }
1420 
1421     @Override
hashCode()1422     public int hashCode() {
1423         return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
1424                 wifiConfiguration.getDefaultSecurityType(),
1425                 wifiConfiguration.getPasspointUniqueId(),
1426                 wifiConfiguration.subscriptionId, wifiConfiguration.carrierId,
1427                 wifiConfiguration.getSubscriptionGroup());
1428     }
1429 
1430     @Override
equals(Object obj)1431     public boolean equals(Object obj) {
1432         if (this == obj) {
1433             return true;
1434         }
1435         if (!(obj instanceof WifiNetworkSuggestion)) {
1436             return false;
1437         }
1438         WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
1439         if (this.passpointConfiguration == null ^ lhs.passpointConfiguration == null) {
1440             return false;
1441         }
1442 
1443         return TextUtils.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
1444                 && TextUtils.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
1445                 && TextUtils.equals(this.wifiConfiguration.getDefaultSecurityType(),
1446                 lhs.wifiConfiguration.getDefaultSecurityType())
1447                 && TextUtils.equals(this.wifiConfiguration.getPasspointUniqueId(),
1448                 lhs.wifiConfiguration.getPasspointUniqueId())
1449                 && this.wifiConfiguration.carrierId == lhs.wifiConfiguration.carrierId
1450                 && this.wifiConfiguration.subscriptionId == lhs.wifiConfiguration.subscriptionId
1451                 && Objects.equals(this.wifiConfiguration.getSubscriptionGroup(),
1452                 lhs.wifiConfiguration.getSubscriptionGroup());
1453     }
1454 
1455     @Override
toString()1456     public String toString() {
1457         StringBuilder sb = new StringBuilder("WifiNetworkSuggestion[ ")
1458                 .append("SSID=").append(wifiConfiguration.SSID)
1459                 .append(", BSSID=").append(wifiConfiguration.BSSID)
1460                 .append(", FQDN=").append(wifiConfiguration.FQDN)
1461                 .append(", SecurityParams=");
1462         wifiConfiguration.getSecurityParamsList().stream()
1463                 .forEach(param -> {
1464                     sb.append(" ");
1465                     sb.append(WifiConfiguration.getSecurityTypeName(param.getSecurityType()));
1466                     if (param.isAddedByAutoUpgrade()) sb.append("^");
1467                 });
1468         sb.append(", isAppInteractionRequired=").append(isAppInteractionRequired)
1469                 .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
1470                 .append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect)
1471                 .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
1472                 .append(", isUnTrusted=").append(!wifiConfiguration.trusted)
1473                 .append(", isOemPaid=").append(wifiConfiguration.oemPaid)
1474                 .append(", isOemPrivate=").append(wifiConfiguration.oemPrivate)
1475                 .append(", isCarrierMerged=").append(wifiConfiguration.carrierMerged)
1476                 .append(", isHiddenSsid=").append(wifiConfiguration.hiddenSSID)
1477                 .append(", priorityGroup=").append(priorityGroup)
1478                 .append(", subscriptionId=").append(wifiConfiguration.subscriptionId)
1479                 .append(", subscriptionGroup=").append(wifiConfiguration.getSubscriptionGroup())
1480                 .append(", carrierId=").append(wifiConfiguration.carrierId)
1481                 .append(", priority=").append(wifiConfiguration.priority)
1482                 .append(", meteredness=").append(wifiConfiguration.meteredOverride)
1483                 .append(", restricted=").append(wifiConfiguration.restricted)
1484                 .append(" ]");
1485         return sb.toString();
1486     }
1487 
1488     /**
1489      * Get the {@link WifiConfiguration} associated with this Suggestion.
1490      * @hide
1491      */
1492     @SystemApi
1493     @NonNull
getWifiConfiguration()1494     public WifiConfiguration getWifiConfiguration() {
1495         return wifiConfiguration;
1496     }
1497 
1498     /**
1499      * Get the BSSID, or null if unset.
1500      * @see Builder#setBssid(MacAddress)
1501      */
1502     @Nullable
getBssid()1503     public MacAddress getBssid() {
1504         if (wifiConfiguration.BSSID == null) {
1505             return null;
1506         }
1507         return MacAddress.fromString(wifiConfiguration.BSSID);
1508     }
1509 
1510     /** @see Builder#setCredentialSharedWithUser(boolean) */
isCredentialSharedWithUser()1511     public boolean isCredentialSharedWithUser() {
1512         return isUserAllowedToManuallyConnect;
1513     }
1514 
1515     /** @see Builder#setIsAppInteractionRequired(boolean) */
isAppInteractionRequired()1516     public boolean isAppInteractionRequired() {
1517         return isAppInteractionRequired;
1518     }
1519 
1520     /** @see Builder#setIsEnhancedOpen(boolean)  */
isEnhancedOpen()1521     public boolean isEnhancedOpen() {
1522         return wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE);
1523     }
1524 
1525     /** @see Builder#setIsHiddenSsid(boolean)  */
isHiddenSsid()1526     public boolean isHiddenSsid() {
1527         return wifiConfiguration.hiddenSSID;
1528     }
1529 
1530     /** @see Builder#setIsInitialAutojoinEnabled(boolean)  */
isInitialAutojoinEnabled()1531     public boolean isInitialAutojoinEnabled() {
1532         return isInitialAutoJoinEnabled;
1533     }
1534 
1535     /** @see Builder#setIsMetered(boolean)  */
isMetered()1536     public boolean isMetered() {
1537         return wifiConfiguration.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED;
1538     }
1539 
1540     /** @see Builder#setIsUserInteractionRequired(boolean)  */
isUserInteractionRequired()1541     public boolean isUserInteractionRequired() {
1542         return isUserInteractionRequired;
1543     }
1544 
1545     /**
1546      * Get the {@link PasspointConfiguration} associated with this Suggestion, or null if this
1547      * Suggestion is not for a Passpoint network.
1548      */
1549     @Nullable
getPasspointConfig()1550     public PasspointConfiguration getPasspointConfig() {
1551         return passpointConfiguration;
1552     }
1553 
1554     /** @see Builder#setPriority(int)  */
1555     @IntRange(from = 0)
getPriority()1556     public int getPriority() {
1557         return wifiConfiguration.priority;
1558     }
1559 
1560     /**
1561      * Return the unicode SSID of the network, or null if this is a Passpoint network or the SSID is
1562      * non-unicode.
1563      * <p>
1564      * Note: use {@link #getWifiSsid()} which supports both unicode and non-unicode SSID.
1565      * @see Builder#setSsid(String)
1566      */
1567     @Nullable
getSsid()1568     public String getSsid() {
1569         if (wifiConfiguration.SSID == null) {
1570             return null;
1571         }
1572         WifiSsid wifiSsid;
1573         try {
1574             wifiSsid = WifiSsid.fromString(wifiConfiguration.SSID);
1575         } catch (IllegalArgumentException e) {
1576             return null;
1577         }
1578         if (wifiSsid.getUtf8Text() == null) {
1579             return null;
1580         }
1581         return wifiSsid.getUtf8Text().toString();
1582     }
1583 
1584     /**
1585      * Return the {@link WifiSsid} of the network, or null if this is a Passpoint network.
1586      * @see Builder#setWifiSsid(WifiSsid)
1587      * @return An object representing the SSID the network. {@code null} for passpoint network.
1588      */
1589     @Nullable
getWifiSsid()1590     public WifiSsid getWifiSsid() {
1591         if (wifiConfiguration.SSID == null) {
1592             return null;
1593         }
1594         WifiSsid wifiSsid;
1595         try {
1596             wifiSsid = WifiSsid.fromString(wifiConfiguration.SSID);
1597         } catch (IllegalArgumentException e) {
1598             throw new IllegalStateException("Invalid SSID in the network suggestion");
1599         }
1600         return wifiSsid;
1601     }
1602 
1603     /** @see Builder#setUntrusted(boolean)  */
isUntrusted()1604     public boolean isUntrusted() {
1605         return !wifiConfiguration.trusted;
1606     }
1607 
1608     /**
1609      * Return if a suggestion is for a restricted network
1610      * @see Builder#setRestricted(boolean)
1611      * @return true if the suggestion is restricted, false otherwise
1612      */
isRestricted()1613     public boolean isRestricted() {
1614         return wifiConfiguration.restricted;
1615     }
1616 
1617     /**
1618      * @see Builder#setOemPaid(boolean)
1619      * @hide
1620      */
1621     @SystemApi
1622     @RequiresApi(Build.VERSION_CODES.S)
isOemPaid()1623     public boolean isOemPaid() {
1624         if (!SdkLevel.isAtLeastS()) {
1625             throw new UnsupportedOperationException();
1626         }
1627         return wifiConfiguration.oemPaid;
1628     }
1629 
1630     /**
1631      * @see Builder#setOemPrivate(boolean)
1632      * @hide
1633      */
1634     @SystemApi
1635     @RequiresApi(Build.VERSION_CODES.S)
isOemPrivate()1636     public boolean isOemPrivate() {
1637         if (!SdkLevel.isAtLeastS()) {
1638             throw new UnsupportedOperationException();
1639         }
1640         return wifiConfiguration.oemPrivate;
1641     }
1642 
1643     /**
1644      * @see Builder#setCarrierMerged(boolean)
1645      */
1646     @RequiresApi(Build.VERSION_CODES.S)
isCarrierMerged()1647     public boolean isCarrierMerged() {
1648         if (!SdkLevel.isAtLeastS()) {
1649             throw new UnsupportedOperationException();
1650         }
1651         return wifiConfiguration.carrierMerged;
1652     }
1653 
1654     /**
1655      * Get the WifiEnterpriseConfig, or null if unset.
1656      * @see Builder#setWapiEnterpriseConfig(WifiEnterpriseConfig)
1657      * @see Builder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)
1658      * @see Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)
1659      */
1660     @Nullable
getEnterpriseConfig()1661     public WifiEnterpriseConfig getEnterpriseConfig() {
1662         if (!wifiConfiguration.isEnterprise()) {
1663             return null;
1664         }
1665         return wifiConfiguration.enterpriseConfig;
1666     }
1667 
1668     /**
1669      * Get the passphrase, or null if unset.
1670      * @see Builder#setWapiPassphrase(String)
1671      * @see Builder#setWpa2Passphrase(String)
1672      * @see Builder#setWpa3Passphrase(String)
1673      */
1674     @Nullable
getPassphrase()1675     public String getPassphrase() {
1676         if (wifiConfiguration.preSharedKey == null) {
1677             return null;
1678         }
1679         return WifiInfo.removeDoubleQuotes(wifiConfiguration.preSharedKey);
1680     }
1681 
1682     /**
1683      * @see Builder#setPriorityGroup(int)
1684      */
1685     @IntRange(from = 0)
getPriorityGroup()1686     public int getPriorityGroup() {
1687         return priorityGroup;
1688     }
1689 
1690     /**
1691      * @see Builder#setSubscriptionId(int)
1692      */
1693     @RequiresApi(Build.VERSION_CODES.S)
getSubscriptionId()1694     public int getSubscriptionId() {
1695         if (!SdkLevel.isAtLeastS()) {
1696             throw new UnsupportedOperationException();
1697         }
1698         return wifiConfiguration.subscriptionId;
1699     }
1700 
1701     /**
1702      * @see Builder#setCarrierId(int)
1703      * @hide
1704      */
1705     @SystemApi
getCarrierId()1706     public int getCarrierId() {
1707         return wifiConfiguration.carrierId;
1708     }
1709 
1710     /**
1711      * Get the MAC randomization method.
1712      * @return one of {@code RANDOMIZATION_*} values
1713      * @see Builder#setMacRandomizationSetting(int)
1714      */
getMacRandomizationSetting()1715     public @MacRandomizationSetting int getMacRandomizationSetting() {
1716         return wifiConfiguration.macRandomizationSetting
1717                 == WifiConfiguration.RANDOMIZATION_NON_PERSISTENT
1718                 ? RANDOMIZATION_NON_PERSISTENT : RANDOMIZATION_PERSISTENT;
1719     }
1720 
1721     /**
1722      * Get the subscription Group UUID of the suggestion
1723      * @see Builder#setSubscriptionGroup(ParcelUuid)
1724      * @return Uuid represent a Subscription Group
1725      */
1726     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
getSubscriptionGroup()1727     public @Nullable ParcelUuid getSubscriptionGroup() {
1728         if (!SdkLevel.isAtLeastT()) {
1729             throw new UnsupportedOperationException();
1730         }
1731         return wifiConfiguration.getSubscriptionGroup();
1732     }
1733 
1734     /**
1735      * See {@link Builder#setWifi7Enabled(boolean)}
1736      */
1737     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
isWifi7Enabled()1738     public boolean isWifi7Enabled() {
1739         return wifiConfiguration.isWifi7Enabled();
1740     }
1741 }
1742