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