1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.app.compat.CompatChanges;
26 import android.compat.annotation.ChangeId;
27 import android.compat.annotation.EnabledAfter;
28 import android.net.MacAddress;
29 import android.net.wifi.util.HexEncoding;
30 import android.os.Build;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.text.TextUtils;
34 import android.util.Log;
35 import android.util.SparseIntArray;
36 
37 import androidx.annotation.RequiresApi;
38 import androidx.annotation.VisibleForTesting;
39 
40 import com.android.internal.util.Preconditions;
41 import com.android.modules.utils.build.SdkLevel;
42 import com.android.wifi.flags.Flags;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.nio.charset.StandardCharsets;
47 import java.util.ArrayList;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Objects;
51 import java.util.Set;
52 import java.util.stream.Collectors;
53 import java.util.stream.IntStream;
54 
55 /**
56  * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
57  *
58  * <p>This is input for the framework provided by a client app, i.e. it exposes knobs to instruct
59  * the framework how it should configure a hotspot.
60  *
61  * <p>System apps can use this to configure a tethered hotspot or local-only hotspot.
62  *
63  * <p>Instances of this class are immutable.
64  */
65 public final class SoftApConfiguration implements Parcelable {
66 
67     private static final String TAG = "SoftApConfiguration";
68 
69     @VisibleForTesting
70     static final int PSK_MIN_LEN = 8;
71 
72     @VisibleForTesting
73     static final int PSK_MAX_LEN = 63;
74 
75     /**
76      * 2GHz band.
77      * @hide
78      */
79     @SystemApi
80     public static final int BAND_2GHZ = 1 << 0;
81 
82     /**
83      * 5GHz band.
84      * @hide
85      */
86     @SystemApi
87     public static final int BAND_5GHZ = 1 << 1;
88 
89     /**
90      * 6GHz band.
91      * @hide
92      */
93     @SystemApi
94     public static final int BAND_6GHZ = 1 << 2;
95 
96     /**
97      * 60GHz band.
98      * @hide
99      */
100     @SystemApi
101     public static final int BAND_60GHZ = 1 << 3;
102 
103     /**
104      * Device is allowed to choose the optimal band (2GHz, 5GHz, 6GHz) based on device capability,
105      * operating country code and current radio conditions.
106      * @hide
107      *
108      * @deprecated This is no longer supported. The value is fixed at
109      * (BAND_2GHZ | BAND_5GHZ | BAND_6GHZ) even if a new band is supported in the future, for
110      * instance {@code BAND_60GHZ}. The bands are a bit mask - use any combination of
111      * {@code BAND_}, for instance {@code BAND_2GHZ | BAND_5GHZ}.
112      */
113     @SystemApi
114     public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;
115 
116     /**
117      * A default value used to configure shut down timeout setting to default value.
118      * See {@link Builder#setShutdownTimeoutMillis(long)} or
119      * {@link Builder#setBridgedModeOpportunisticShutdownTimeoutMillis(long)} for details.
120      *
121      * @hide
122      */
123     @SystemApi
124     public static final long DEFAULT_TIMEOUT = -1;
125 
126     /** @hide */
127     @Retention(RetentionPolicy.SOURCE)
128     @IntDef(flag = true, prefix = { "BAND_TYPE_" }, value = {
129             BAND_2GHZ,
130             BAND_5GHZ,
131             BAND_6GHZ,
132             BAND_60GHZ,
133     })
134     public @interface BandType {}
135 
136     /**
137      * All of the supported band types.
138      * @hide
139      */
140     public static int[] BAND_TYPES = {BAND_2GHZ, BAND_5GHZ, BAND_6GHZ, BAND_60GHZ};
141 
isBandValid(@andType int band)142     private static boolean isBandValid(@BandType int band) {
143         int bandAny = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ | BAND_60GHZ;
144         return ((band != 0) && ((band & ~bandAny) == 0));
145     }
146 
147     private static final int MIN_CH_2G_BAND = 1;
148     private static final int MAX_CH_2G_BAND = 14;
149     private static final int MIN_CH_5G_BAND = 34;
150     private static final int MAX_CH_5G_BAND = 196;
151     private static final int MIN_CH_6G_BAND = 1;
152     private static final int MAX_CH_6G_BAND = 253;
153     private static final int MIN_CH_60G_BAND = 1;
154     private static final int MAX_CH_60G_BAND = 6;
155 
156     /**
157      * Requires to configure MAC randomization setting to None when configuring BSSID.
158      */
159     @ChangeId
160     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
161     private static final long FORCE_MUTUAL_EXCLUSIVE_BSSID_MAC_RAMDONIZATION_SETTING = 215656264L;
162 
163     /**
164      * Removes zero support on
165      * {@link android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)}.
166      *
167      * @hide
168      */
169     @ChangeId
170     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
171     public static final long REMOVE_ZERO_FOR_TIMEOUT_SETTING = 213289672L;
172 
isChannelBandPairValid(int channel, @BandType int band)173     private static boolean isChannelBandPairValid(int channel, @BandType int band) {
174         switch (band) {
175             case BAND_2GHZ:
176                 if (channel < MIN_CH_2G_BAND || channel >  MAX_CH_2G_BAND) {
177                     return false;
178                 }
179                 break;
180 
181             case BAND_5GHZ:
182                 if (channel < MIN_CH_5G_BAND || channel >  MAX_CH_5G_BAND) {
183                     return false;
184                 }
185                 break;
186 
187             case BAND_6GHZ:
188                 if (channel < MIN_CH_6G_BAND || channel >  MAX_CH_6G_BAND) {
189                     return false;
190                 }
191                 break;
192 
193             case BAND_60GHZ:
194                 if (channel < MIN_CH_60G_BAND || channel >  MAX_CH_60G_BAND) {
195                     return false;
196                 }
197                 break;
198 
199             default:
200                 return false;
201         }
202         return true;
203     }
204 
205     /**
206      * SSID for the AP, or null for a framework-determined SSID.
207      */
208     private final @Nullable WifiSsid mWifiSsid;
209 
210     /**
211      * BSSID for the AP, or null to use a framework-determined BSSID.
212      */
213     private final @Nullable MacAddress mBssid;
214 
215     /**
216      * Vendor elements for the AP, structured as dd+len+elements
217      */
218     private final @NonNull List<ScanResult.InformationElement> mVendorElements;
219 
220     /**
221      * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on
222      * the security type.
223      */
224     private final @Nullable String mPassphrase;
225 
226     /**
227      * This is a network that does not broadcast its SSID, so an
228      * SSID-specific probe request must be used for scans.
229      */
230     private final boolean mHiddenSsid;
231 
232     /**
233      * The operating channels of the dual APs.
234      *
235      * The SparseIntArray that consists the band and the channel of matching the band.
236      */
237     @NonNull
238     private final SparseIntArray mChannels;
239 
240     /**
241      * The set of allowed channels in 2.4GHz band to select from using ACS (Automatic Channel
242      * Selection) algorithm.
243      *
244      * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}.
245      * Otherwise, this set will be ignored.
246      *
247      * If the set is empty, then all channels in 2.4GHz band are allowed.
248      */
249     private final @NonNull Set<Integer> mAllowedAcsChannels2g;
250 
251     /**
252      * The set of allowed channels in 5GHz band to select from using ACS (Automatic Channel
253      * Selection) algorithm.
254      *
255      * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}.
256      * Otherwise, this set will be ignored.
257      *
258      * If the set is empty, then all channels in 5GHz are allowed.
259      */
260     private final @NonNull Set<Integer> mAllowedAcsChannels5g;
261 
262     /**
263      * The set of allowed channels in 6GHz band to select from using ACS (Automatic Channel
264      * Selection) algorithm.
265      *
266      * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}.
267      * Otherwise, this set will be ignored.
268      *
269      * If the set is empty, then all channels in 6GHz are allowed.
270      */
271     private final @NonNull Set<Integer> mAllowedAcsChannels6g;
272 
273     /**
274      * The maximum channel bandwidth for SoftAp operation
275      *
276      * Default value is SoftApInfo#CHANNEL_WIDTH_AUTO which means the channel bandwidth
277      * is to be selected by the chip based on device capabilities.
278      * <p>
279      *
280      * Valid values: {@link SoftApInfo#CHANNEL_WIDTH_AUTO},
281      * {@link SoftApInfo#CHANNEL_WIDTH_20MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_40MHZ},
282      * {@link SoftApInfo#CHANNEL_WIDTH_80MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_160MHZ},
283      * {@link SoftApInfo#CHANNEL_WIDTH_320MHZ}
284      *
285      */
286     private final @WifiAnnotations.Bandwidth int mMaxChannelBandwidth;
287 
288     /**
289      * The maximim allowed number of clients that can associate to the AP.
290      */
291     private final int mMaxNumberOfClients;
292 
293     /**
294      * The operating security type of the AP.
295      * One of the following security types:
296      * {@link #SECURITY_TYPE_OPEN},
297      * {@link #SECURITY_TYPE_WPA2_PSK},
298      * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
299      * {@link #SECURITY_TYPE_WPA3_SAE},
300      * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION},
301      * {@link #SECURITY_TYPE_WPA3_OWE}
302      */
303     private final @SecurityType int mSecurityType;
304 
305     /**
306      * The flag to indicate client need to authorize by user
307      * when client is connecting to AP.
308      */
309     private final boolean mClientControlByUser;
310 
311     /**
312      * The list of blocked client that can't associate to the AP.
313      */
314     private final List<MacAddress> mBlockedClientList;
315 
316     /**
317      * The list of allowed client that can associate to the AP.
318      */
319     private final List<MacAddress> mAllowedClientList;
320 
321     /**
322      * Whether auto shutdown of soft AP is enabled or not.
323      */
324     private final boolean mAutoShutdownEnabled;
325 
326     /**
327      * Delay in milliseconds before shutting down soft AP when
328      * there are no connected devices.
329      */
330     private final long mShutdownTimeoutMillis;
331 
332     /** @hide */
333     @Retention(RetentionPolicy.SOURCE)
334     @IntDef(prefix = {"RANDOMIZATION_"}, value = {
335             RANDOMIZATION_NONE,
336             RANDOMIZATION_PERSISTENT,
337             RANDOMIZATION_NON_PERSISTENT})
338     public @interface MacRandomizationSetting {}
339 
340     /**
341      * Use the factory MAC address as the BSSID of the AP.
342      *
343      * @hide
344      */
345     @SystemApi
346     public static final int RANDOMIZATION_NONE = 0;
347 
348     /**
349      * Generate a persistent randomized MAC address as the BSSID of the AP.
350      * The MAC address is persisted per SSID - i.e. as long as the SSID of the AP doesn't change
351      * then it will have a persistent MAC address (which is initially random and is not the factory
352      * MAC address).
353      *
354      * @hide
355      */
356     @SystemApi
357     public static final int RANDOMIZATION_PERSISTENT = 1;
358 
359     /**
360      * Generate a randomized MAC address as the BSSID of the AP. The MAC address is not persisted
361      * - it is re-generated every time the AP is re-enabled.
362      * @hide
363      */
364     @SystemApi
365     public static final int RANDOMIZATION_NON_PERSISTENT = 2;
366 
367     /**
368      * Level of MAC randomization for the AP BSSID.
369      */
370     @MacRandomizationSetting
371     private int mMacRandomizationSetting;
372 
373 
374     /**
375      * Whether opportunistic shutdown of an instance in bridged AP is enabled or not.
376      */
377     private boolean mBridgedModeOpportunisticShutdownEnabled;
378 
379     /**
380      * Whether 802.11ax AP is enabled or not.
381      */
382     private boolean mIeee80211axEnabled;
383 
384     /**
385      * Whether 802.11be AP is enabled or not.
386      */
387     private boolean mIeee80211beEnabled;
388 
389     /**
390      * Whether the current configuration is configured by user or not.
391      */
392     private boolean mIsUserConfiguration;
393 
394     /**
395      * Randomized MAC address to use with this configuration when MAC randomization setting
396      * is {@link #RANDOMIZATION_PERSISTENT}.
397      */
398     private final @Nullable MacAddress mPersistentRandomizedMacAddress;
399 
400     /**
401      * Delay in milliseconds before shutting down an instance in bridged AP.
402      */
403     private final long mBridgedModeOpportunisticShutdownTimeoutMillis;
404 
405     /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */
406     private @NonNull List<OuiKeyedData> mVendorData;
407 
408     /**
409      * THe definition of security type OPEN.
410      */
411     public static final int SECURITY_TYPE_OPEN = 0;
412 
413     /**
414      * The definition of security type WPA2-PSK.
415      */
416     public static final int SECURITY_TYPE_WPA2_PSK = 1;
417 
418     /**
419      * The definition of security type WPA3-SAE Transition mode.
420      */
421     public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2;
422 
423     /**
424      * The definition of security type WPA3-SAE.
425      */
426     public static final int SECURITY_TYPE_WPA3_SAE = 3;
427 
428     /**
429      * The definition of security type WPA3-OWE Transition.
430      */
431     public static final int SECURITY_TYPE_WPA3_OWE_TRANSITION = 4;
432 
433     /**
434      * The definition of security type WPA3-OWE.
435      */
436     public static final int SECURITY_TYPE_WPA3_OWE = 5;
437 
438     /** @hide */
439     @Retention(RetentionPolicy.SOURCE)
440     @IntDef(prefix = { "SECURITY_TYPE_" }, value = {
441         SECURITY_TYPE_OPEN,
442         SECURITY_TYPE_WPA2_PSK,
443         SECURITY_TYPE_WPA3_SAE_TRANSITION,
444         SECURITY_TYPE_WPA3_SAE,
445         SECURITY_TYPE_WPA3_OWE_TRANSITION,
446         SECURITY_TYPE_WPA3_OWE,
447     })
448     public @interface SecurityType {}
449 
450     /** Private constructor for Builder and Parcelable implementation. */
SoftApConfiguration( @ullable WifiSsid ssid, @Nullable MacAddress bssid, @Nullable String passphrase, boolean hiddenSsid, @NonNull SparseIntArray channels, @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, long shutdownTimeoutMillis, boolean clientControlByUser, @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList, int macRandomizationSetting, boolean bridgedModeOpportunisticShutdownEnabled, boolean ieee80211axEnabled, boolean ieee80211beEnabled, boolean isUserConfiguration, long bridgedModeOpportunisticShutdownTimeoutMillis, @NonNull List<ScanResult.InformationElement> vendorElements, @Nullable MacAddress persistentRandomizedMacAddress, @NonNull Set<Integer> allowedAcsChannels24g, @NonNull Set<Integer> allowedAcsChannels5g, @NonNull Set<Integer> allowedAcsChannels6g, @WifiAnnotations.Bandwidth int maxChannelBandwidth, @Nullable List<OuiKeyedData> vendorData)451     private SoftApConfiguration(
452             @Nullable WifiSsid ssid,
453             @Nullable MacAddress bssid,
454             @Nullable String passphrase,
455             boolean hiddenSsid,
456             @NonNull SparseIntArray channels,
457             @SecurityType int securityType,
458             int maxNumberOfClients,
459             boolean shutdownTimeoutEnabled,
460             long shutdownTimeoutMillis,
461             boolean clientControlByUser,
462             @NonNull List<MacAddress> blockedList,
463             @NonNull List<MacAddress> allowedList,
464             int macRandomizationSetting,
465             boolean bridgedModeOpportunisticShutdownEnabled,
466             boolean ieee80211axEnabled,
467             boolean ieee80211beEnabled,
468             boolean isUserConfiguration,
469             long bridgedModeOpportunisticShutdownTimeoutMillis,
470             @NonNull List<ScanResult.InformationElement> vendorElements,
471             @Nullable MacAddress persistentRandomizedMacAddress,
472             @NonNull Set<Integer> allowedAcsChannels24g,
473             @NonNull Set<Integer> allowedAcsChannels5g,
474             @NonNull Set<Integer> allowedAcsChannels6g,
475             @WifiAnnotations.Bandwidth int maxChannelBandwidth,
476             @Nullable List<OuiKeyedData> vendorData) {
477         mWifiSsid = ssid;
478         mBssid = bssid;
479         mPassphrase = passphrase;
480         mHiddenSsid = hiddenSsid;
481         if (channels.size() != 0) {
482             mChannels = channels.clone();
483         } else {
484             mChannels = new SparseIntArray(1);
485             mChannels.put(BAND_2GHZ, 0);
486         }
487         mSecurityType = securityType;
488         mMaxNumberOfClients = maxNumberOfClients;
489         mAutoShutdownEnabled = shutdownTimeoutEnabled;
490         mShutdownTimeoutMillis = shutdownTimeoutMillis;
491         mClientControlByUser = clientControlByUser;
492         mBlockedClientList = new ArrayList<>(blockedList);
493         mAllowedClientList = new ArrayList<>(allowedList);
494         mMacRandomizationSetting = macRandomizationSetting;
495         mBridgedModeOpportunisticShutdownEnabled = bridgedModeOpportunisticShutdownEnabled;
496         mIeee80211axEnabled = ieee80211axEnabled;
497         mIeee80211beEnabled = ieee80211beEnabled;
498         mIsUserConfiguration = isUserConfiguration;
499         mBridgedModeOpportunisticShutdownTimeoutMillis =
500                 bridgedModeOpportunisticShutdownTimeoutMillis;
501         mVendorElements = new ArrayList<>(vendorElements);
502         mPersistentRandomizedMacAddress = persistentRandomizedMacAddress;
503         mAllowedAcsChannels2g = new HashSet<>(allowedAcsChannels24g);
504         mAllowedAcsChannels5g = new HashSet<>(allowedAcsChannels5g);
505         mAllowedAcsChannels6g = new HashSet<>(allowedAcsChannels6g);
506         mMaxChannelBandwidth = maxChannelBandwidth;
507         mVendorData = new ArrayList<>(vendorData);
508     }
509 
510     @Override
equals(Object otherObj)511     public boolean equals(Object otherObj) {
512         if (this == otherObj) {
513             return true;
514         }
515         if (!(otherObj instanceof SoftApConfiguration)) {
516             return false;
517         }
518         SoftApConfiguration other = (SoftApConfiguration) otherObj;
519         return Objects.equals(mWifiSsid, other.mWifiSsid)
520                 && Objects.equals(mBssid, other.mBssid)
521                 && Objects.equals(mPassphrase, other.mPassphrase)
522                 && mHiddenSsid == other.mHiddenSsid
523                 && mChannels.toString().equals(other.mChannels.toString())
524                 && mSecurityType == other.mSecurityType
525                 && mMaxNumberOfClients == other.mMaxNumberOfClients
526                 && mAutoShutdownEnabled == other.mAutoShutdownEnabled
527                 && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
528                 && mClientControlByUser == other.mClientControlByUser
529                 && Objects.equals(mBlockedClientList, other.mBlockedClientList)
530                 && Objects.equals(mAllowedClientList, other.mAllowedClientList)
531                 && mMacRandomizationSetting == other.mMacRandomizationSetting
532                 && mBridgedModeOpportunisticShutdownEnabled
533                         == other.mBridgedModeOpportunisticShutdownEnabled
534                 && mIeee80211axEnabled == other.mIeee80211axEnabled
535                 && mIeee80211beEnabled == other.mIeee80211beEnabled
536                 && mIsUserConfiguration == other.mIsUserConfiguration
537                 && mBridgedModeOpportunisticShutdownTimeoutMillis
538                         == other.mBridgedModeOpportunisticShutdownTimeoutMillis
539                 && Objects.equals(mVendorElements, other.mVendorElements)
540                 && Objects.equals(
541                         mPersistentRandomizedMacAddress, other.mPersistentRandomizedMacAddress)
542                 && Objects.equals(mAllowedAcsChannels2g, other.mAllowedAcsChannels2g)
543                 && Objects.equals(mAllowedAcsChannels5g, other.mAllowedAcsChannels5g)
544                 && Objects.equals(mAllowedAcsChannels6g, other.mAllowedAcsChannels6g)
545                 && mMaxChannelBandwidth == other.mMaxChannelBandwidth
546                 && Objects.equals(mVendorData, other.mVendorData);
547     }
548 
549     @Override
hashCode()550     public int hashCode() {
551         return Objects.hash(
552                 mWifiSsid,
553                 mBssid,
554                 mPassphrase,
555                 mHiddenSsid,
556                 mChannels.toString(),
557                 mSecurityType,
558                 mMaxNumberOfClients,
559                 mAutoShutdownEnabled,
560                 mShutdownTimeoutMillis,
561                 mClientControlByUser,
562                 mBlockedClientList,
563                 mAllowedClientList,
564                 mMacRandomizationSetting,
565                 mBridgedModeOpportunisticShutdownEnabled,
566                 mIeee80211axEnabled,
567                 mIeee80211beEnabled,
568                 mIsUserConfiguration,
569                 mBridgedModeOpportunisticShutdownTimeoutMillis,
570                 mVendorElements,
571                 mPersistentRandomizedMacAddress,
572                 mAllowedAcsChannels2g,
573                 mAllowedAcsChannels5g,
574                 mAllowedAcsChannels6g,
575                 mMaxChannelBandwidth,
576                 mVendorData);
577     }
578 
579     @Override
toString()580     public String toString() {
581         StringBuilder sbuf = new StringBuilder();
582         sbuf.append("ssid = ").append(mWifiSsid == null ? null : mWifiSsid.toString());
583         if (mBssid != null) sbuf.append(" \n bssid = ").append(mBssid.toString());
584         sbuf.append(" \n Passphrase = ").append(
585                 TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>");
586         sbuf.append(" \n HiddenSsid = ").append(mHiddenSsid);
587         sbuf.append(" \n Channels = ").append(mChannels);
588         sbuf.append(" \n SecurityType = ").append(getSecurityType());
589         sbuf.append(" \n MaxClient = ").append(mMaxNumberOfClients);
590         sbuf.append(" \n AutoShutdownEnabled = ").append(mAutoShutdownEnabled);
591         sbuf.append(" \n ShutdownTimeoutMillis = ").append(mShutdownTimeoutMillis);
592         sbuf.append(" \n ClientControlByUser = ").append(mClientControlByUser);
593         sbuf.append(" \n BlockedClientList = ").append(mBlockedClientList);
594         sbuf.append(" \n AllowedClientList= ").append(mAllowedClientList);
595         sbuf.append(" \n MacRandomizationSetting = ").append(mMacRandomizationSetting);
596         sbuf.append(" \n BridgedModeInstanceOpportunisticEnabled = ")
597                 .append(mBridgedModeOpportunisticShutdownEnabled);
598         sbuf.append(" \n BridgedModeOpportunisticShutdownTimeoutMillis = ")
599                 .append(mBridgedModeOpportunisticShutdownTimeoutMillis);
600         sbuf.append(" \n Ieee80211axEnabled = ").append(mIeee80211axEnabled);
601         sbuf.append(" \n Ieee80211beEnabled = ").append(mIeee80211beEnabled);
602         sbuf.append(" \n isUserConfiguration = ").append(mIsUserConfiguration);
603         sbuf.append(" \n vendorElements = ").append(mVendorElements);
604         sbuf.append(" \n mPersistentRandomizedMacAddress = ")
605                 .append(mPersistentRandomizedMacAddress);
606         sbuf.append(" \n mAllowedAcsChannels2g = ").append(mAllowedAcsChannels2g);
607         sbuf.append(" \n mAllowedAcsChannels5g = ").append(mAllowedAcsChannels5g);
608         sbuf.append(" \n mAllowedAcsChannels6g = ").append(mAllowedAcsChannels6g);
609         sbuf.append(" \n mMaxChannelBandwidth = ").append(mMaxChannelBandwidth);
610         sbuf.append(" \n mVendorData = ").append(mVendorData);
611         return sbuf.toString();
612     }
613 
614     @Override
writeToParcel(@onNull Parcel dest, int flags)615     public void writeToParcel(@NonNull Parcel dest, int flags) {
616         dest.writeParcelable(mWifiSsid, 0);
617         dest.writeParcelable(mBssid, flags);
618         dest.writeString(mPassphrase);
619         dest.writeBoolean(mHiddenSsid);
620         writeSparseIntArray(dest, mChannels);
621         dest.writeInt(mSecurityType);
622         dest.writeInt(mMaxNumberOfClients);
623         dest.writeBoolean(mAutoShutdownEnabled);
624         dest.writeLong(mShutdownTimeoutMillis);
625         dest.writeBoolean(mClientControlByUser);
626         dest.writeTypedList(mBlockedClientList);
627         dest.writeTypedList(mAllowedClientList);
628         dest.writeInt(mMacRandomizationSetting);
629         dest.writeBoolean(mBridgedModeOpportunisticShutdownEnabled);
630         dest.writeBoolean(mIeee80211axEnabled);
631         dest.writeBoolean(mIeee80211beEnabled);
632         dest.writeBoolean(mIsUserConfiguration);
633         dest.writeLong(mBridgedModeOpportunisticShutdownTimeoutMillis);
634         dest.writeTypedList(mVendorElements);
635         dest.writeParcelable(mPersistentRandomizedMacAddress, flags);
636         writeHashSetInt(dest, mAllowedAcsChannels2g);
637         writeHashSetInt(dest, mAllowedAcsChannels5g);
638         writeHashSetInt(dest, mAllowedAcsChannels6g);
639         dest.writeInt(mMaxChannelBandwidth);
640         dest.writeList(mVendorData);
641     }
642 
643     /* Reference from frameworks/base/core/java/android/os/Parcel.java */
writeSparseIntArray(@onNull Parcel dest, @Nullable SparseIntArray val)644     private static void writeSparseIntArray(@NonNull Parcel dest,
645             @Nullable SparseIntArray val) {
646         if (val == null) {
647             dest.writeInt(-1);
648             return;
649         }
650         int n = val.size();
651         dest.writeInt(n);
652         int i = 0;
653         while (i < n) {
654             dest.writeInt(val.keyAt(i));
655             dest.writeInt(val.valueAt(i));
656             i++;
657         }
658     }
659 
660     /* Reference from frameworks/base/core/java/android/os/Parcel.java */
661     @NonNull
readSparseIntArray(@onNull Parcel in)662     private static SparseIntArray readSparseIntArray(@NonNull Parcel in) {
663         int n = in.readInt();
664         if (n < 0) {
665             return new SparseIntArray();
666         }
667         SparseIntArray sa = new SparseIntArray(n);
668         while (n > 0) {
669             int key = in.readInt();
670             int value = in.readInt();
671             sa.append(key, value);
672             n--;
673         }
674         return sa;
675     }
676 
677     /* Write HashSet<Integer> into Parcel */
writeHashSetInt(@onNull Parcel dest, @NonNull Set<Integer> set)678     private static void writeHashSetInt(@NonNull Parcel dest, @NonNull Set<Integer> set) {
679         if (set.isEmpty()) {
680             dest.writeInt(-1);
681             return;
682         }
683 
684         dest.writeInt(set.size());
685         for (int val : set) {
686             dest.writeInt(val);
687         }
688     }
689 
690     /* Read HashSet<Integer> from Parcel */
691     @NonNull
readHashSetInt(@onNull Parcel in)692     private static Set<Integer> readHashSetInt(@NonNull Parcel in) {
693         Set<Integer> set = new HashSet<>();
694         int len = in.readInt();
695         if (len < 0) {
696             return set;
697         }
698 
699         for (int i = 0; i < len; i++) {
700             set.add(in.readInt());
701         }
702         return set;
703     }
704 
705     /* Read List<OuiKeyedData> from Parcel */
706     @NonNull
readOuiKeyedDataList(@onNull Parcel in)707     private static List<OuiKeyedData> readOuiKeyedDataList(@NonNull Parcel in) {
708         List<OuiKeyedData> dataList = new ArrayList<>();
709         if (SdkLevel.isAtLeastT()) {
710             in.readList(dataList, OuiKeyedData.class.getClassLoader(), OuiKeyedData.class);
711         } else {
712             in.readList(dataList, OuiKeyedData.class.getClassLoader());
713         }
714         return dataList;
715     }
716 
717     @Override
describeContents()718     public int describeContents() {
719         return 0;
720     }
721 
722     @NonNull
723     public static final Creator<SoftApConfiguration> CREATOR =
724             new Creator<SoftApConfiguration>() {
725                 @Override
726                 public SoftApConfiguration createFromParcel(Parcel in) {
727                     return new SoftApConfiguration(
728                             in.readParcelable(WifiSsid.class.getClassLoader()),
729                             in.readParcelable(MacAddress.class.getClassLoader()),
730                             in.readString(),
731                             in.readBoolean(),
732                             readSparseIntArray(in),
733                             in.readInt(),
734                             in.readInt(),
735                             in.readBoolean(),
736                             in.readLong(),
737                             in.readBoolean(),
738                             in.createTypedArrayList(MacAddress.CREATOR),
739                             in.createTypedArrayList(MacAddress.CREATOR),
740                             in.readInt(),
741                             in.readBoolean(),
742                             in.readBoolean(),
743                             in.readBoolean(),
744                             in.readBoolean(),
745                             in.readLong(),
746                             in.createTypedArrayList(ScanResult.InformationElement.CREATOR),
747                             in.readParcelable(MacAddress.class.getClassLoader()),
748                             readHashSetInt(in),
749                             readHashSetInt(in),
750                             readHashSetInt(in),
751                             in.readInt(),
752                             readOuiKeyedDataList(in));
753                 }
754 
755                 @Override
756                 public SoftApConfiguration[] newArray(int size) {
757                     return new SoftApConfiguration[size];
758                 }
759             };
760 
761     /**
762      * Return the UTF-8 String set to be the SSID for the AP. If the SSID cannot be decoded as
763      * UTF-8, then this will return {@link WifiManager#UNKNOWN_SSID}.
764      *
765      * @deprecated Use {@link #getWifiSsid()} instead.
766      */
767     @Nullable
768     @Deprecated
getSsid()769     public String getSsid() {
770         if (mWifiSsid == null) {
771             return null;
772         }
773         CharSequence utf8Text = mWifiSsid.getUtf8Text();
774         return utf8Text != null ? utf8Text.toString() : WifiManager.UNKNOWN_SSID;
775     }
776 
777     /**
778      * Return WifiSsid set to be the SSID for the AP.
779      */
780     @Nullable
getWifiSsid()781     public WifiSsid getWifiSsid() {
782         return mWifiSsid;
783     }
784 
785     /**
786      * Return VendorElements for the AP.
787      * @hide
788      */
789     @NonNull
790     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
791     @SystemApi
getVendorElements()792     public List<ScanResult.InformationElement> getVendorElements() {
793         if (!SdkLevel.isAtLeastT()) {
794             throw new UnsupportedOperationException();
795         }
796         return getVendorElementsInternal();
797     }
798 
799     /**
800      * @see #getVendorElements()
801      * @hide
802      */
getVendorElementsInternal()803     public List<ScanResult.InformationElement> getVendorElementsInternal() {
804         return new ArrayList<>(mVendorElements);
805     }
806 
807     /**
808      * Returns MAC address set to be BSSID for the AP.
809      */
810     @Nullable
getBssid()811     public MacAddress getBssid() {
812         return mBssid;
813     }
814 
815     /**
816      * Returns String set to be passphrase for current AP.
817      */
818     @Nullable
getPassphrase()819     public String getPassphrase() {
820         return mPassphrase;
821     }
822 
823     /**
824      * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
825      * not (false: broadcasts its SSID) for the AP.
826      */
isHiddenSsid()827     public boolean isHiddenSsid() {
828         return mHiddenSsid;
829     }
830 
831     /**
832      * Returns band type set to be the band for the AP.
833      *
834      * One or combination of {@code BAND_}, for instance
835      * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, or {@code BAND_2GHZ | BAND_5GHZ}.
836      *
837      * Note: Returns the lowest band when more than one band is set.
838      * Use {@link #getChannels()} to get dual bands setting.
839      *
840      * See also {@link Builder#setBand(int)}.
841      *
842      * @deprecated This API is deprecated. Use {@link #getChannels()} instead.
843      * @hide
844      */
845     @Deprecated
846     @SystemApi
getBand()847     public @BandType int getBand() {
848         return mChannels.keyAt(0);
849     }
850 
851     /**
852      * Returns a sorted array in ascending order that consists of the configured band types
853      * for the APs.
854      *
855      * The band type is one or combination of {@code BAND_}, for instance
856      * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, or {@code BAND_2GHZ | BAND_5GHZ}.
857      *
858      * Note: return array may only include one band when current setting is single AP mode.
859      * See also {@link Builder#setBands(int[])}.
860      *
861      * @hide
862      */
getBands()863     public @NonNull int[] getBands() {
864         int[] bands = new int[mChannels.size()];
865         for (int i = 0; i < bands.length; i++) {
866             bands[i] = mChannels.keyAt(i);
867         }
868         return bands;
869     }
870 
871     /**
872      * Returns Integer set to be the channel for the AP.
873      *
874      * Note: Returns the channel which associated to the lowest band if more than one channel
875      * is set. Use {@link Builder#getChannels()} to get dual channel setting.
876      * See also {@link Builder#setChannel(int, int)}.
877      *
878      * @deprecated This API is deprecated. Use {@link #getChannels()} instead.
879      * @hide
880      */
881     @Deprecated
882     @SystemApi
getChannel()883     public int getChannel() {
884         return mChannels.valueAt(0);
885     }
886 
887 
888     /**
889      * Returns SparseIntArray (key: {@code BandType} , value: channel) that consists of
890      * the configured bands and channels for the AP(s).
891      *
892      * The returned channel value is Wi-Fi channel numbering.
893      * Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016
894      * specifications, section 17.3.8.4.2, 17.3.8.4.3 and Table 15-6.
895      *
896      * Note: return array may only include one channel when current setting is single AP mode.
897      * See also {@link Builder#setChannels(SparseIntArray)}.
898      *
899      * @hide
900      */
901     @RequiresApi(Build.VERSION_CODES.S)
902     @SystemApi
getChannels()903     public @NonNull SparseIntArray getChannels() {
904         if (!SdkLevel.isAtLeastS()) {
905             throw new UnsupportedOperationException();
906         }
907         return mChannels.clone();
908     }
909 
910     /**
911      * Get security type params which depends on which security passphrase to set.
912      *
913      * @return One of:
914      * {@link #SECURITY_TYPE_OPEN},
915      * {@link #SECURITY_TYPE_WPA2_PSK},
916      * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
917      * {@link #SECURITY_TYPE_WPA3_SAE},
918      * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION},
919      * {@link #SECURITY_TYPE_WPA3_OWE}
920      */
getSecurityType()921     public @SecurityType int getSecurityType() {
922         return mSecurityType;
923     }
924 
925     /**
926      * Returns the maximum number of clients that can associate to the AP.
927      * See also {@link Builder#setMaxNumberOfClients(int)}.
928      *
929      * @hide
930      */
931     @SystemApi
getMaxNumberOfClients()932     public int getMaxNumberOfClients() {
933         return mMaxNumberOfClients;
934     }
935 
936     /**
937      * Returns whether auto shutdown is enabled or not.
938      * The Soft AP will shutdown when there are no devices associated to it for
939      * the timeout duration. See also {@link Builder#setAutoShutdownEnabled(boolean)}.
940      *
941      * @hide
942      */
943     @SystemApi
isAutoShutdownEnabled()944     public boolean isAutoShutdownEnabled() {
945         return mAutoShutdownEnabled;
946     }
947 
948     /**
949      * Returns the shutdown timeout in milliseconds.
950      * The Soft AP will shutdown when there are no devices associated to it for
951      * the timeout duration. See also {@link Builder#setShutdownTimeoutMillis(long)}.
952      *
953      * @hide
954      */
955     @SystemApi
getShutdownTimeoutMillis()956     public long getShutdownTimeoutMillis() {
957         if (!CompatChanges.isChangeEnabled(
958                 REMOVE_ZERO_FOR_TIMEOUT_SETTING) && mShutdownTimeoutMillis == DEFAULT_TIMEOUT) {
959             // For legacy application, return 0 when setting is DEFAULT_TIMEOUT.
960             return 0;
961         }
962         return mShutdownTimeoutMillis;
963     }
964 
965     /**
966      * Returns a flag indicating whether clients need to be pre-approved by the user.
967      * (true: authorization required) or not (false: not required).
968      * See also {@link Builder#setClientControlByUserEnabled(Boolean)}.
969      *
970      * @hide
971      */
972     @SystemApi
isClientControlByUserEnabled()973     public boolean isClientControlByUserEnabled() {
974         return mClientControlByUser;
975     }
976 
977     /**
978      * Returns List of clients which aren't allowed to associate to the AP.
979      *
980      * Clients are configured using {@link Builder#setBlockedClientList(List)}
981      *
982      * @hide
983      */
984     @NonNull
985     @SystemApi
getBlockedClientList()986     public List<MacAddress> getBlockedClientList() {
987         return mBlockedClientList;
988     }
989 
990     /**
991      * List of clients which are allowed to associate to the AP.
992      * Clients are configured using {@link Builder#setAllowedClientList(List)}
993      *
994      * @hide
995      */
996     @NonNull
997     @SystemApi
getAllowedClientList()998     public List<MacAddress> getAllowedClientList() {
999         return mAllowedClientList;
1000     }
1001 
1002     /**
1003      * Returns the level of MAC randomization for the AP BSSID.
1004      * See also {@link Builder#setMacRandomizationSetting(int)}.
1005      *
1006      * @hide
1007      */
1008     @RequiresApi(Build.VERSION_CODES.S)
1009     @SystemApi
1010     @MacRandomizationSetting
getMacRandomizationSetting()1011     public int getMacRandomizationSetting() {
1012         if (!SdkLevel.isAtLeastS()) {
1013             throw new UnsupportedOperationException();
1014         }
1015         return getMacRandomizationSettingInternal();
1016     }
1017 
1018     /**
1019      * @hide
1020      */
1021     @MacRandomizationSetting
getMacRandomizationSettingInternal()1022     public int getMacRandomizationSettingInternal() {
1023         return mMacRandomizationSetting;
1024     }
1025 
1026     /**
1027      * Returns whether opportunistic shutdown of an instance in bridged AP is enabled or not.
1028      *
1029      * See also {@link Builder#setBridgedModeOpportunisticShutdownEnabled(boolean}}
1030      * @hide
1031      */
1032     @RequiresApi(Build.VERSION_CODES.S)
1033     @SystemApi
isBridgedModeOpportunisticShutdownEnabled()1034     public boolean isBridgedModeOpportunisticShutdownEnabled() {
1035         if (!SdkLevel.isAtLeastS()) {
1036             throw new UnsupportedOperationException();
1037         }
1038         return isBridgedModeOpportunisticShutdownEnabledInternal();
1039     }
1040 
1041     /**
1042      * @see #isBridgedModeOpportunisticShutdownEnabled()
1043      * @hide
1044      */
isBridgedModeOpportunisticShutdownEnabledInternal()1045     public boolean isBridgedModeOpportunisticShutdownEnabledInternal() {
1046         return mBridgedModeOpportunisticShutdownEnabled;
1047     }
1048 
1049     /**
1050      * @see #isIeee80211axEnabled()
1051      * @hide
1052      */
isIeee80211axEnabledInternal()1053     public boolean isIeee80211axEnabledInternal() {
1054         return mIeee80211axEnabled;
1055     }
1056 
1057     /**
1058      * Returns whether or not 802.11ax is enabled on the SoftAP.
1059      * This is an indication that if the device support 802.11ax AP then to enable or disable
1060      * that feature. If the device does not support 802.11ax AP then this flag is ignored.
1061      * See also {@link Builder#setIeee80211axEnabled(boolean}}
1062      * @hide
1063      */
1064     @RequiresApi(Build.VERSION_CODES.S)
1065     @SystemApi
isIeee80211axEnabled()1066     public boolean isIeee80211axEnabled() {
1067         if (!SdkLevel.isAtLeastS()) {
1068             throw new UnsupportedOperationException();
1069         }
1070         return isIeee80211axEnabledInternal();
1071     }
1072 
1073     /**
1074      * Returns whether or not the Soft AP is configured to enable 802.11be.
1075      * This is an indication that if the device support 802.11be AP then to enable or disable
1076      * that feature. If the device does not support 802.11be AP then this flag is ignored.
1077      * See also {@link Builder#setIeee80211beEnabled(boolean}}
1078      * @hide
1079      */
1080     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
1081     @SystemApi
isIeee80211beEnabled()1082     public boolean isIeee80211beEnabled() {
1083         if (!SdkLevel.isAtLeastT()) {
1084             throw new UnsupportedOperationException();
1085         }
1086         return mIeee80211beEnabled;
1087     }
1088 
1089     /**
1090      * Returns the allowed channels for ACS in a selected band.
1091      *
1092      * If an empty array is returned, then all channels in that band are allowed
1093      * The channels are configured using {@link Builder#setAllowedAcsChannels(int, int[])}
1094      *
1095      * @param band one of the following band types:
1096      * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
1097      *
1098      * @return array of the allowed channels for ACS in that band
1099      *
1100      * @hide
1101      */
1102     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
1103     @NonNull
1104     @SystemApi
getAllowedAcsChannels(@andType int band)1105     public int[] getAllowedAcsChannels(@BandType int band) {
1106         if (!SdkLevel.isAtLeastT()) {
1107             throw new UnsupportedOperationException();
1108         }
1109         switch(band) {
1110             case BAND_2GHZ:
1111                 return mAllowedAcsChannels2g.stream().mapToInt(Integer::intValue).toArray();
1112             case BAND_5GHZ:
1113                 return mAllowedAcsChannels5g.stream().mapToInt(Integer::intValue).toArray();
1114             case BAND_6GHZ:
1115                 return mAllowedAcsChannels6g.stream().mapToInt(Integer::intValue).toArray();
1116             default:
1117                 throw new IllegalArgumentException("getAllowedAcsChannels: Invalid band: " + band);
1118         }
1119     }
1120 
1121     /**
1122      * Returns configured maximum channel bandwidth for the SoftAp connection.
1123      *
1124      * If not configured, it will return {@link SoftApInfo#CHANNEL_WIDTH_AUTO}
1125      *
1126      * @hide
1127      */
1128     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
1129     @SystemApi
getMaxChannelBandwidth()1130     public @WifiAnnotations.Bandwidth int getMaxChannelBandwidth() {
1131         if (!SdkLevel.isAtLeastT()) {
1132             throw new UnsupportedOperationException();
1133         }
1134         return mMaxChannelBandwidth;
1135     }
1136 
1137     /**
1138      * Returns whether or not the {@link SoftApConfiguration} was configured by the user
1139      * (as opposed to the default system configuration).
1140      * <p>
1141      * The {@link SoftApConfiguration} is considered user edited once the
1142      * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)} is called
1143      * - whether or not that configuration is the same as the default system configuration!
1144      *
1145      * @hide
1146      */
1147     @RequiresApi(Build.VERSION_CODES.S)
1148     @SystemApi
isUserConfiguration()1149     public boolean isUserConfiguration() {
1150         if (!SdkLevel.isAtLeastS()) {
1151             throw new UnsupportedOperationException();
1152         }
1153         return isUserConfigurationInternal();
1154     }
1155 
1156     /**
1157      * Returns the randomized MAC address to be used by this configuration.
1158      *
1159      * The Soft AP may be configured to use a persistent randomized MAC address with
1160      * {@link Builder#setMacRandomizationSetting(int)}. This method returns the persistent
1161      * randomized MAC address which will be used for the Soft AP controlled by this configuration.
1162      *
1163      * @hide
1164      */
1165     @SystemApi
getPersistentRandomizedMacAddress()1166     public @NonNull MacAddress getPersistentRandomizedMacAddress() {
1167         return mPersistentRandomizedMacAddress;
1168     }
1169 
1170     /**
1171      * @hide
1172      */
isUserConfigurationInternal()1173     public boolean isUserConfigurationInternal() {
1174         return mIsUserConfiguration;
1175     }
1176 
1177     /**
1178      * Returns the bridged mode opportunistic shutdown timeout in milliseconds.
1179      * An instance in bridged AP will shutdown when there is no device associated to it for
1180      * the timeout duration. See also
1181      * {@link Builder#setBridgedModeOpportunisticShutdownTimeoutMillis(long)}.
1182      *
1183      * @hide
1184      */
1185     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
1186     @SystemApi
getBridgedModeOpportunisticShutdownTimeoutMillis()1187     public long getBridgedModeOpportunisticShutdownTimeoutMillis() {
1188         if (!SdkLevel.isAtLeastT()) {
1189             throw new UnsupportedOperationException();
1190         }
1191         return mBridgedModeOpportunisticShutdownTimeoutMillis;
1192     }
1193 
1194 
1195     /**
1196      * @hide
1197      */
getBridgedModeOpportunisticShutdownTimeoutMillisInternal()1198     public long getBridgedModeOpportunisticShutdownTimeoutMillisInternal() {
1199         return mBridgedModeOpportunisticShutdownTimeoutMillis;
1200     }
1201 
1202     /**
1203      * Return the vendor-provided configuration data, if it exists. See also {@link
1204      * Builder#setVendorData(List)}
1205      *
1206      * @return Vendor configuration data, or empty list if it does not exist.
1207      * @hide
1208      */
1209     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
1210     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
1211     @NonNull
1212     @SystemApi
getVendorData()1213     public List<OuiKeyedData> getVendorData() {
1214         if (!SdkLevel.isAtLeastV()) {
1215             throw new UnsupportedOperationException();
1216         }
1217         return mVendorData;
1218     }
1219 
1220     /**
1221      * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}.
1222      * Note that SoftApConfiguration may contain configuration which is cannot be represented
1223      * by the legacy WifiConfiguration, in such cases a null will be returned.
1224      *
1225      * To maintain legacy behavior, the SSID of the WifiConfiguration will be the UTF-8
1226      * representation of the SSID without double quotes, as opposed to the double-quoted UTF-8
1227      * format documented in {@link WifiConfiguration#SSID}. If the SSID cannot be decoded as UTF-8,
1228      * then the SSID of the WifiConfiguration will be {@link WifiManager#UNKNOWN_SSID}.
1229      *
1230      * <li> SoftAp band in {@link WifiConfiguration.apBand} only supports
1231      * 2GHz, 5GHz, 2GHz+5GHz bands, so conversion is limited to these bands. </li>
1232      *
1233      * <li> SoftAp security type in {@link WifiConfiguration.KeyMgmt} only supports
1234      * NONE, WPA2_PSK, so conversion is limited to these security type.</li>
1235      * @hide
1236      */
1237     @Nullable
1238     @SystemApi
toWifiConfiguration()1239     public WifiConfiguration toWifiConfiguration() {
1240         WifiConfiguration wifiConfig = new WifiConfiguration();
1241         CharSequence utf8Text = mWifiSsid != null ? mWifiSsid.getUtf8Text() : null;
1242         wifiConfig.SSID = utf8Text != null ? utf8Text.toString() : WifiManager.UNKNOWN_SSID;
1243         wifiConfig.preSharedKey = mPassphrase;
1244         wifiConfig.hiddenSSID = mHiddenSsid;
1245         wifiConfig.apChannel = getChannel();
1246         switch (mSecurityType) {
1247             case SECURITY_TYPE_OPEN:
1248                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1249                 break;
1250             case SECURITY_TYPE_WPA2_PSK:
1251             case SECURITY_TYPE_WPA3_SAE_TRANSITION:
1252                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
1253                 break;
1254             default:
1255                 Log.e(TAG, "Convert fail, unsupported security type :" + mSecurityType);
1256                 return null;
1257         }
1258 
1259         switch (getBand()) {
1260             case BAND_2GHZ:
1261                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_2GHZ;
1262                 break;
1263             case BAND_5GHZ:
1264                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_5GHZ;
1265                 break;
1266             case BAND_2GHZ | BAND_5GHZ:
1267                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
1268                 break;
1269             case BAND_ANY:
1270                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
1271                 break;
1272             default:
1273                 Log.e(TAG, "Convert fail, unsupported band setting :" + getBand());
1274                 return null;
1275         }
1276         return wifiConfig;
1277     }
1278 
1279     /**
1280      * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
1281      * Soft AP.
1282      *
1283      * All fields are optional. By default, SSID and BSSID are automatically chosen by the
1284      * framework, and an open network is created.
1285      *
1286      * @hide
1287      */
1288     @SystemApi
1289     public static final class Builder {
1290         private WifiSsid mWifiSsid;
1291         private MacAddress mBssid;
1292         private String mPassphrase;
1293         private boolean mHiddenSsid;
1294         private SparseIntArray mChannels;
1295         private int mMaxNumberOfClients;
1296         private int mSecurityType;
1297         private boolean mAutoShutdownEnabled;
1298         private long mShutdownTimeoutMillis;
1299         private boolean mClientControlByUser;
1300         private List<MacAddress> mBlockedClientList;
1301         private List<MacAddress> mAllowedClientList;
1302         private int mMacRandomizationSetting;
1303         private boolean mBridgedModeOpportunisticShutdownEnabled;
1304         private boolean mIeee80211axEnabled;
1305         private boolean mIeee80211beEnabled;
1306         private boolean mIsUserConfiguration;
1307         private long mBridgedModeOpportunisticShutdownTimeoutMillis;
1308         private List<ScanResult.InformationElement> mVendorElements;
1309         private MacAddress mPersistentRandomizedMacAddress;
1310         private Set<Integer> mAllowedAcsChannels2g;
1311         private Set<Integer> mAllowedAcsChannels5g;
1312         private Set<Integer> mAllowedAcsChannels6g;
1313         private @WifiAnnotations.Bandwidth int mMaxChannelBandwidth;
1314         private @Nullable List<OuiKeyedData> mVendorData;
1315 
1316         /**
1317          * Constructs a Builder with default values (see {@link Builder}).
1318          */
Builder()1319         public Builder() {
1320             mWifiSsid = null;
1321             mBssid = null;
1322             mPassphrase = null;
1323             mHiddenSsid = false;
1324             mChannels = new SparseIntArray(1);
1325             mChannels.put(BAND_2GHZ, 0);
1326             mMaxNumberOfClients = 0;
1327             mSecurityType = SECURITY_TYPE_OPEN;
1328             mAutoShutdownEnabled = true; // enabled by default.
1329             mShutdownTimeoutMillis = DEFAULT_TIMEOUT;
1330             mClientControlByUser = false;
1331             mBlockedClientList = new ArrayList<>();
1332             mAllowedClientList = new ArrayList<>();
1333             if (SdkLevel.isAtLeastT()) {
1334                 mMacRandomizationSetting = RANDOMIZATION_NON_PERSISTENT;
1335             } else {
1336                 mMacRandomizationSetting = RANDOMIZATION_PERSISTENT;
1337             }
1338             mBridgedModeOpportunisticShutdownEnabled = true;
1339             mIeee80211axEnabled = true;
1340             mIeee80211beEnabled = true;
1341             mIsUserConfiguration = true;
1342             mBridgedModeOpportunisticShutdownTimeoutMillis = DEFAULT_TIMEOUT;
1343             mVendorElements = new ArrayList<>();
1344             mPersistentRandomizedMacAddress = null;
1345             mAllowedAcsChannels2g = new HashSet<>();
1346             mAllowedAcsChannels5g = new HashSet<>();
1347             mAllowedAcsChannels6g = new HashSet<>();
1348             mMaxChannelBandwidth = SoftApInfo.CHANNEL_WIDTH_AUTO;
1349             mVendorData = new ArrayList<>();
1350         }
1351 
1352         /**
1353          * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
1354          */
Builder(@onNull SoftApConfiguration other)1355         public Builder(@NonNull SoftApConfiguration other) {
1356             if (other == null) {
1357                 Log.e(TAG, "Cannot provide a null SoftApConfiguration");
1358                 return;
1359             }
1360 
1361             mWifiSsid = other.mWifiSsid;
1362             mBssid = other.mBssid;
1363             mPassphrase = other.mPassphrase;
1364             mHiddenSsid = other.mHiddenSsid;
1365             mChannels = other.mChannels.clone();
1366             mMaxNumberOfClients = other.mMaxNumberOfClients;
1367             mSecurityType = other.mSecurityType;
1368             mAutoShutdownEnabled = other.mAutoShutdownEnabled;
1369             mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
1370             mClientControlByUser = other.mClientControlByUser;
1371             mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
1372             mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
1373             mMacRandomizationSetting = other.mMacRandomizationSetting;
1374             mBridgedModeOpportunisticShutdownEnabled =
1375                     other.mBridgedModeOpportunisticShutdownEnabled;
1376             mIeee80211axEnabled = other.mIeee80211axEnabled;
1377             mIeee80211beEnabled = other.mIeee80211beEnabled;
1378             mIsUserConfiguration = other.mIsUserConfiguration;
1379             mBridgedModeOpportunisticShutdownTimeoutMillis =
1380                     other.mBridgedModeOpportunisticShutdownTimeoutMillis;
1381             mVendorElements = new ArrayList<>(other.mVendorElements);
1382             mPersistentRandomizedMacAddress = other.mPersistentRandomizedMacAddress;
1383             mAllowedAcsChannels2g = new HashSet<>(other.mAllowedAcsChannels2g);
1384             mAllowedAcsChannels5g = new HashSet<>(other.mAllowedAcsChannels5g);
1385             mAllowedAcsChannels6g = new HashSet<>(other.mAllowedAcsChannels6g);
1386             mMaxChannelBandwidth = other.mMaxChannelBandwidth;
1387             if (SdkLevel.isAtLeastS() && mBssid != null) {
1388                 // Auto set correct MAC randomization setting for the legacy SoftApConfiguration
1389                 // to avoid the exception happen when framework (system server) copy
1390                 // SoftApConfiguration.
1391                 mMacRandomizationSetting = RANDOMIZATION_NONE;
1392             }
1393             mVendorData = new ArrayList<>(other.mVendorData);
1394         }
1395 
1396         /**
1397          * Builds the {@link SoftApConfiguration}.
1398          *
1399          * @return A new {@link SoftApConfiguration}, as configured by previous method calls.
1400          */
1401         @NonNull
build()1402         public SoftApConfiguration build() {
1403             for (MacAddress client : mAllowedClientList) {
1404                 if (mBlockedClientList.contains(client)) {
1405                     throw new IllegalArgumentException("A MacAddress exist in both client list");
1406                 }
1407             }
1408 
1409             // mMacRandomizationSetting supported from S.
1410             if (SdkLevel.isAtLeastS() && CompatChanges.isChangeEnabled(
1411                     FORCE_MUTUAL_EXCLUSIVE_BSSID_MAC_RAMDONIZATION_SETTING)
1412                     && mBssid != null && mMacRandomizationSetting != RANDOMIZATION_NONE) {
1413                 throw new IllegalArgumentException("A BSSID had configured but MAC randomization"
1414                         + " setting is not NONE");
1415             }
1416 
1417             if (!CompatChanges.isChangeEnabled(
1418                     REMOVE_ZERO_FOR_TIMEOUT_SETTING) && mShutdownTimeoutMillis == DEFAULT_TIMEOUT) {
1419                 mShutdownTimeoutMillis = 0; // Use 0 for legacy app.
1420             }
1421             return new SoftApConfiguration(
1422                     mWifiSsid,
1423                     mBssid,
1424                     mPassphrase,
1425                     mHiddenSsid,
1426                     mChannels,
1427                     mSecurityType,
1428                     mMaxNumberOfClients,
1429                     mAutoShutdownEnabled,
1430                     mShutdownTimeoutMillis,
1431                     mClientControlByUser,
1432                     mBlockedClientList,
1433                     mAllowedClientList,
1434                     mMacRandomizationSetting,
1435                     mBridgedModeOpportunisticShutdownEnabled,
1436                     mIeee80211axEnabled,
1437                     mIeee80211beEnabled,
1438                     mIsUserConfiguration,
1439                     mBridgedModeOpportunisticShutdownTimeoutMillis,
1440                     mVendorElements,
1441                     mPersistentRandomizedMacAddress,
1442                     mAllowedAcsChannels2g,
1443                     mAllowedAcsChannels5g,
1444                     mAllowedAcsChannels6g,
1445                     mMaxChannelBandwidth,
1446                     mVendorData);
1447         }
1448 
1449         /**
1450          * Specifies a UTF-8 SSID for the AP.
1451          * <p>
1452          * Null SSID only support when configure a local-only hotspot.
1453          * <p>
1454          * <li>If not set, defaults to null.</li>
1455          *
1456          * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
1457          *             chosen by the framework.
1458          * @return Builder for chaining.
1459          * @throws IllegalArgumentException when the SSID is empty, not unicode, or if the byte
1460          *                                  representation is longer than 32 bytes.
1461          *
1462          * @deprecated Use {@link #setWifiSsid(WifiSsid)} instead.
1463          */
1464         @NonNull
1465         @Deprecated
setSsid(@ullable String ssid)1466         public Builder setSsid(@Nullable String ssid) {
1467             if (ssid == null) {
1468                 mWifiSsid = null;
1469                 return this;
1470             }
1471 
1472             Preconditions.checkStringNotEmpty(ssid);
1473             Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
1474             mWifiSsid = WifiSsid.fromUtf8Text(ssid);
1475             return this;
1476         }
1477 
1478         /**
1479          * Specifies an SSID for the AP in the form of WifiSsid.
1480          * <p>
1481          * Null SSID only support when configure a local-only hotspot.
1482          * <p>
1483          * <li>If not set, defaults to null.</li>
1484          *
1485          * @param wifiSsid SSID, or null ot have the SSID automatically chosen by the framework.
1486          * @return Builder for chaining.
1487          */
1488         @NonNull
1489         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setWifiSsid(@ullable WifiSsid wifiSsid)1490         public Builder setWifiSsid(@Nullable WifiSsid wifiSsid) {
1491             if (!SdkLevel.isAtLeastT()) {
1492                 throw new UnsupportedOperationException();
1493             }
1494             mWifiSsid = wifiSsid;
1495             return this;
1496         }
1497 
1498         /**
1499          * Specify vendor-specific information elements for the (Soft) AP to transmit in its beacons
1500          * and probe responses. Method also validates the structure and throws
1501          * IllegalArgumentException in cases when ID of IE is not 0xDD (221) or incoming list
1502          * contain duplicate elements.
1503          *
1504          * @param vendorElements VendorElements
1505          * @return Builder for chaining.
1506          */
1507         @NonNull
1508         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setVendorElements( @onNull List<ScanResult.InformationElement> vendorElements)1509         public Builder setVendorElements(
1510                 @NonNull List<ScanResult.InformationElement> vendorElements) {
1511             if (!SdkLevel.isAtLeastT()) {
1512                 throw new UnsupportedOperationException();
1513             }
1514             for (ScanResult.InformationElement e : vendorElements) {
1515                 if (e.id != ScanResult.InformationElement.EID_VSA) {
1516                     throw new IllegalArgumentException("received InformationElement which is not "
1517                             + "related to VendorElements. VendorElement block should start with "
1518                             + HexEncoding.encodeToString(
1519                                     new byte[]{ (byte) ScanResult.InformationElement.EID_VSA }));
1520                 }
1521             }
1522             final HashSet<ScanResult.InformationElement> set = new HashSet<>(vendorElements);
1523             if (set.size() < vendorElements.size()) {
1524                 throw new IllegalArgumentException("vendor elements array contain duplicates. "
1525                         + "Please avoid passing duplicated and keep structure clean.");
1526             }
1527             mVendorElements = new ArrayList<>(vendorElements);
1528             return this;
1529         }
1530 
1531         /**
1532          * Specifies a BSSID for the AP.
1533          * <p>
1534          * <li>If not set, defaults to null.</li>
1535          *
1536          * When this method is called, the caller needs to configure MAC randomization settings to
1537          * {@link #RANDOMIZATION_NONE}. See {@link #setMacRandomizationSetting(int)} for details.
1538          *
1539          * If multiple bands are requested via {@link #setBands(int[])} or
1540          * {@link #setChannels(SparseIntArray)}, HAL will derive 2 MAC addresses since framework
1541          * only sends down 1 MAC address.
1542          *
1543          * An example (but different implementation may perform a different mapping):
1544          * <li>MAC address 1: copy value of MAC address,
1545          * and set byte 1 = (0xFF - BSSID[1])</li>
1546          * <li>MAC address 2: copy value of MAC address,
1547          * and set byte 2 = (0xFF - BSSID[2])</li>
1548          *
1549          * Example BSSID argument: e2:38:60:c4:0e:b7
1550          * Derived MAC address 1: e2:c7:60:c4:0e:b7
1551          * Derived MAC address 2: e2:38:9f:c4:0e:b7
1552          *
1553          * <p>
1554          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
1555          * {@link SoftApCapability#areFeaturesSupported(long)}
1556          * with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine
1557          * whether or not this feature is supported.
1558          *
1559          * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
1560          *              responsible for avoiding collisions.
1561          * @return Builder for chaining.
1562          * @throws IllegalArgumentException when the given BSSID is the all-zero
1563          *                                  , multicast or broadcast MAC address.
1564          */
1565         @NonNull
setBssid(@ullable MacAddress bssid)1566         public Builder setBssid(@Nullable MacAddress bssid) {
1567             if (bssid != null) {
1568                 Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
1569                 if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) {
1570                     throw new IllegalArgumentException("bssid doesn't support "
1571                             + "multicast or broadcast mac address");
1572                 }
1573             }
1574             mBssid = bssid;
1575             return this;
1576         }
1577 
1578         /**
1579          * Specifies that this AP should use specific security type with the given ASCII passphrase.
1580          *
1581          * @param securityType One of the following security types:
1582          * {@link #SECURITY_TYPE_OPEN},
1583          * {@link #SECURITY_TYPE_WPA2_PSK},
1584          * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
1585          * {@link #SECURITY_TYPE_WPA3_SAE},
1586          * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION},
1587          * {@link #SECURITY_TYPE_WPA3_OWE}.
1588          * @param passphrase The passphrase to use for sepcific {@code securityType} configuration
1589          * or null with {@link #SECURITY_TYPE_OPEN}, {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION},
1590          * and {@link #SECURITY_TYPE_WPA3_OWE}.
1591          *
1592          * @return Builder for chaining.
1593          * @throws IllegalArgumentException when the passphrase is non-null for
1594          *             - {@link #SECURITY_TYPE_OPEN}
1595          *             - {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION}
1596          *             - {@link #SECURITY_TYPE_WPA3_OWE}
1597          * @throws IllegalArgumentException when the passphrase is empty for
1598          *             - {@link #SECURITY_TYPE_WPA2_PSK},
1599          *             - {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
1600          *             - {@link #SECURITY_TYPE_WPA3_SAE},
1601          * @throws IllegalArgumentException before {@link android.os.Build.VERSION_CODES#TIRAMISU})
1602          *         when the passphrase is not between 8 and 63 bytes (inclusive) for
1603          *             - {@link #SECURITY_TYPE_WPA2_PSK}
1604          *             - {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION}
1605          */
1606         @NonNull
setPassphrase(@ullable String passphrase, @SecurityType int securityType)1607         public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
1608             if (!SdkLevel.isAtLeastT()
1609                     && (securityType == SECURITY_TYPE_WPA3_OWE_TRANSITION
1610                             || securityType == SECURITY_TYPE_WPA3_OWE)) {
1611                 throw new UnsupportedOperationException();
1612             }
1613             if (securityType == SECURITY_TYPE_OPEN
1614                     || securityType == SECURITY_TYPE_WPA3_OWE_TRANSITION
1615                     || securityType == SECURITY_TYPE_WPA3_OWE) {
1616                 if (passphrase != null) {
1617                     throw new IllegalArgumentException(
1618                             "passphrase should be null when security type is open");
1619                 }
1620             } else {
1621                 Preconditions.checkStringNotEmpty(passphrase);
1622                 if (!SdkLevel.isAtLeastT() && (securityType == SECURITY_TYPE_WPA2_PSK
1623                         || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION)) {
1624                     int passphraseByteLength = 0;
1625                     if (!TextUtils.isEmpty(passphrase)) {
1626                         passphraseByteLength = passphrase.getBytes(StandardCharsets.UTF_8).length;
1627                     }
1628                     if (passphraseByteLength < PSK_MIN_LEN || passphraseByteLength > PSK_MAX_LEN) {
1629                         throw new IllegalArgumentException(
1630                                 "Passphrase length must be at least " + PSK_MIN_LEN
1631                                         + " and no more than " + PSK_MAX_LEN
1632                                         + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
1633                     }
1634                 }
1635             }
1636             mSecurityType = securityType;
1637             mPassphrase = passphrase;
1638             return this;
1639         }
1640 
1641         /**
1642          * Specifies whether the AP is hidden (doesn't broadcast its SSID) or
1643          * not (broadcasts its SSID).
1644          * <p>
1645          * <li>If not set, defaults to false (i.e not a hidden network).</li>
1646          *
1647          * @param hiddenSsid true for a hidden SSID, false otherwise.
1648          * @return Builder for chaining.
1649          */
1650         @NonNull
setHiddenSsid(boolean hiddenSsid)1651         public Builder setHiddenSsid(boolean hiddenSsid) {
1652             mHiddenSsid = hiddenSsid;
1653             return this;
1654         }
1655 
1656         /**
1657          * Specifies the band for the AP.
1658          * <p>
1659          * <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
1660          *
1661          * @param band One or combination of the following band type:
1662          * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
1663          * @return Builder for chaining.
1664          * @throws IllegalArgumentException when an invalid band type is provided.
1665          */
1666         @NonNull
setBand(@andType int band)1667         public Builder setBand(@BandType int band) {
1668             if (!isBandValid(band)) {
1669                 throw new IllegalArgumentException("Invalid band type: " + band);
1670             }
1671             mChannels = new SparseIntArray(1);
1672             mChannels.put(band, 0);
1673             return this;
1674         }
1675 
1676         /**
1677          * Specifies the bands for the APs.
1678          * If more than 1 band is set, this will bring up concurrent APs.
1679          * on the requested bands (if possible).
1680          * <p>
1681          *
1682          * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
1683          * whether or not concurrent APs are supported.
1684          *
1685          * Requires the driver to support {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
1686          * when multiple bands are configured. Otherwise,
1687          * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
1688          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1689          *
1690          * Note: Only supports 2.4GHz + 5GHz bands. If any other band is set, will report error
1691          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1692          *
1693          * @param bands Array of the {@link #BandType}.
1694          * @return Builder for chaining.
1695          * @throws IllegalArgumentException when more than 2 bands are set or an invalid band type
1696          *                                  is provided.
1697          */
1698         @RequiresApi(Build.VERSION_CODES.S)
1699         @NonNull
setBands(@onNull int[] bands)1700         public Builder setBands(@NonNull int[] bands) {
1701             if (!SdkLevel.isAtLeastS()) {
1702                 throw new UnsupportedOperationException();
1703             }
1704             if (bands.length == 0 || bands.length > 2) {
1705                 throw new IllegalArgumentException("Unsupported number of bands("
1706                         + bands.length + ") configured");
1707             }
1708             SparseIntArray channels = new SparseIntArray(bands.length);
1709             for (int val : bands) {
1710                 if (!isBandValid(val)) {
1711                     throw new IllegalArgumentException("Invalid band type: " + val);
1712                 }
1713                 channels.put(val, 0);
1714             }
1715             mChannels = channels;
1716             return this;
1717         }
1718 
1719 
1720         /**
1721          * Specifies the channel and associated band for the AP.
1722          *
1723          * The channel which AP resides on. Valid channels are country dependent.
1724          * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
1725          * valid channels.
1726          *
1727          * <p>
1728          * If not set, the default for the channel is the special value 0 which has the
1729          * framework auto-select a valid channel from the band configured with
1730          * {@link #setBand(int)}.
1731          *
1732          * The channel auto selection will be offloaded to driver when
1733          * {@link SoftApCapability#areFeaturesSupported(long)}
1734          * with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
1735          * return true. The driver will auto select the best channel (e.g. best performance)
1736          * based on environment interference. Check {@link SoftApCapability} for more detail.
1737          *
1738          * The API contains (band, channel) input since the 6GHz band uses the same channel
1739          * numbering scheme as is used in the 2.4GHz and 5GHz band. Therefore, both are needed to
1740          * uniquely identify individual channels.
1741          *
1742          * <p>
1743          * @param channel operating channel of the AP.
1744          * @param band containing this channel.
1745          * @return Builder for chaining.
1746          * @throws IllegalArgumentException when the invalid channel or band type is configured.
1747          */
1748         @NonNull
setChannel(int channel, @BandType int band)1749         public Builder setChannel(int channel, @BandType int band) {
1750             if (!isChannelBandPairValid(channel, band)) {
1751                 throw new IllegalArgumentException("Invalid channel(" + channel
1752                         + ") & band (" + band + ") configured");
1753             }
1754             mChannels = new SparseIntArray(1);
1755             mChannels.put(band, channel);
1756             return this;
1757         }
1758 
1759         /**
1760          * Specifies the channels and associated bands for the APs.
1761          *
1762          * When more than 1 channel is set, this will bring up concurrent APs on the requested
1763          * channels and bands (if possible).
1764          *
1765          * Valid channels are country dependent.
1766          * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
1767          * valid channels in each band.
1768          *
1769          * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
1770          * whether or not concurrent APs are supported.
1771          *
1772          * <p>
1773          * If not set, the default for the channel is the special value 0 which has the framework
1774          * auto-select a valid channel from the band configured with {@link #setBands(int[])}.
1775          *
1776          * The channel auto selection will be offloaded to driver when
1777          * {@link SoftApCapability#areFeaturesSupported(long)}
1778          * with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
1779          * returns true. The driver will auto select the best channel (e.g. best performance)
1780          * based on environment interference. Check {@link SoftApCapability} for more detail.
1781          *
1782          * Requires the driver to support {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
1783          * when multiple bands are configured without specified channel value (i.e. channel is
1784          * the special value 0). Otherwise,
1785          * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
1786          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1787          *
1788          * Note: Only supports 2.4GHz + 5GHz bands. If any other band is set, will report error
1789          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1790          *
1791          * The API contains (band, channel) input since the 6GHz band uses the same channel
1792          * numbering scheme as is used in the 2.4GHz and 5GHz band. Therefore, both are needed to
1793          * uniquely identify individual channels.
1794          *
1795          * Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016
1796          * specifications, section 17.3.8.4.2, 17.3.8.4.3 and Table 15-6.
1797          *
1798          * <p>
1799          * @param channels SparseIntArray (key: {@code #BandType} , value: channel) consists of
1800          *                 {@code BAND_} and corresponding channel.
1801          * @return Builder for chaining.
1802          * @throws IllegalArgumentException when more than 2 channels are set or the invalid
1803          *                                  channel or band type is configured.
1804          */
1805         @RequiresApi(Build.VERSION_CODES.S)
1806         @NonNull
setChannels(@onNull SparseIntArray channels)1807         public Builder setChannels(@NonNull SparseIntArray channels) {
1808             if (!SdkLevel.isAtLeastS()) {
1809                 throw new UnsupportedOperationException();
1810             }
1811             if (channels.size() == 0 || channels.size() > 2) {
1812                 throw new IllegalArgumentException("Unsupported number of channels("
1813                         + channels.size() + ") configured");
1814             }
1815             for (int i = 0; i < channels.size(); i++) {
1816                 int channel = channels.valueAt(i);
1817                 int band = channels.keyAt(i);
1818                 if (channel == 0) {
1819                     if (!isBandValid(band)) {
1820                         throw new IllegalArgumentException("Invalid band type: " + band);
1821                     }
1822                 } else {
1823                     if (!isChannelBandPairValid(channel, band)) {
1824                         throw new IllegalArgumentException("Invalid channel(" + channel
1825                                 + ") & band (" + band + ") configured");
1826                     }
1827                 }
1828             }
1829             mChannels = channels.clone();
1830             return this;
1831         }
1832 
1833 
1834         /**
1835          * Specifies the maximum number of clients that can associate to the AP.
1836          *
1837          * The maximum number of clients (STAs) which can associate to the AP.
1838          * The AP will reject association from any clients above this number.
1839          * Specify a value of 0 to have the framework automatically use the maximum number
1840          * which the device can support (based on hardware and carrier constraints).
1841          * <p>
1842          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
1843          * {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients
1844          * which the device supports (based on hardware and carrier constraints).
1845          *
1846          * <p>
1847          * <li>If not set, defaults to 0.</li>
1848          *
1849          * This method requires HAL support. If the method is used to set a
1850          * non-zero {@code maxNumberOfClients} value then
1851          * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
1852          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1853          *
1854          * <p>
1855          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
1856          * {@link SoftApCapability#areFeaturesSupported(long)}
1857          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
1858          * or not this feature is supported.
1859          *
1860          * @param maxNumberOfClients maximum client number of the AP.
1861          * @return Builder for chaining.
1862          */
1863         @NonNull
setMaxNumberOfClients(@ntRangefrom = 0) int maxNumberOfClients)1864         public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
1865             if (maxNumberOfClients < 0) {
1866                 throw new IllegalArgumentException("maxNumberOfClients should be not negative");
1867             }
1868             mMaxNumberOfClients = maxNumberOfClients;
1869             return this;
1870         }
1871 
1872         /**
1873          * Specifies whether auto shutdown is enabled or not.
1874          * The Soft AP will shut down when there are no devices connected to it for
1875          * the timeout duration.
1876          *
1877          * <p>
1878          * <li>If not set, defaults to true</li>
1879          *
1880          * @param enable true to enable, false to disable.
1881          * @return Builder for chaining.
1882          *
1883          * @see #setShutdownTimeoutMillis(long)
1884          */
1885         @NonNull
setAutoShutdownEnabled(boolean enable)1886         public Builder setAutoShutdownEnabled(boolean enable) {
1887             mAutoShutdownEnabled = enable;
1888             return this;
1889         }
1890 
1891         /**
1892          * Specifies the shutdown timeout in milliseconds.
1893          * The Soft AP will shut down when there are no devices connected to it for
1894          * the timeout duration.
1895          *
1896          * Specify a value of {@link #DEFAULT_TIMEOUT} to have the framework automatically use
1897          * default timeout setting which defined in
1898          * {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
1899          *
1900          * <p>
1901          * <li>If not set, defaults to {@link #DEFAULT_TIMEOUT}</li>
1902          * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
1903          * set to true</li>
1904          *
1905          * @param timeoutMillis milliseconds of the timeout delay. Any value less than 1 is invalid
1906          *                      except {@link #DEFAULT_TIMEOUT}.
1907          * @return Builder for chaining.
1908          *
1909          * @see #setAutoShutdownEnabled(boolean)
1910          */
1911         @NonNull
setShutdownTimeoutMillis(@ntRangefrom = -1) long timeoutMillis)1912         public Builder setShutdownTimeoutMillis(@IntRange(from = -1) long timeoutMillis) {
1913             if (CompatChanges.isChangeEnabled(
1914                     REMOVE_ZERO_FOR_TIMEOUT_SETTING) && timeoutMillis < 1) {
1915                 if (timeoutMillis != DEFAULT_TIMEOUT) {
1916                     throw new IllegalArgumentException("Invalid timeout value: " + timeoutMillis);
1917                 }
1918             } else if (timeoutMillis < 0) {
1919                 throw new IllegalArgumentException("Invalid timeout value from legacy app: "
1920                         + timeoutMillis);
1921             }
1922             mShutdownTimeoutMillis = timeoutMillis;
1923             return this;
1924         }
1925 
1926         /**
1927          * Configure the Soft AP to require manual user control of client association.
1928          * If disabled (the default) then any client which isn't in the blocked list
1929          * {@link #getBlockedClientList()} can associate to this Soft AP using the
1930          * correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier,
1931          * or user limited - using {@link #setMaxNumberOfClients(int)}).
1932          *
1933          * If manual user control is enabled then clients will be accepted, rejected, or require
1934          * a user approval based on the configuration provided by
1935          * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
1936          *
1937          * <p>
1938          * This method requires HAL support. HAL support can be determined using
1939          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
1940          * {@link SoftApCapability#areFeaturesSupported(long)}
1941          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
1942          *
1943          * <p>
1944          * If the method is called on a device without HAL support then starting the soft AP
1945          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
1946          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
1947          *
1948          * <p>
1949          * <li>If not set, defaults to false (i.e The authoriztion is not required).</li>
1950          *
1951          * @param enabled true for enabling the control by user, false otherwise.
1952          * @return Builder for chaining.
1953          */
1954         @NonNull
setClientControlByUserEnabled(boolean enabled)1955         public Builder setClientControlByUserEnabled(boolean enabled) {
1956             mClientControlByUser = enabled;
1957             return this;
1958         }
1959 
1960         /**
1961          * Configures the set of channel numbers in the specified band that are allowed
1962          * to be selected by the Automatic Channel Selection (ACS) algorithm.
1963          * <p>
1964          *
1965          * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}.
1966          * Otherwise, these sets will be ignored.
1967          * <p>
1968          *
1969          * @param band one of the following band types:
1970          * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
1971          *
1972          * @param channels that are allowed to be used by ACS algorithm in this band. If it is
1973          * configured to an empty array or not configured, then all channels within that band
1974          * will be allowed.
1975          * <p>
1976          *
1977          * @return Builder for chaining.
1978          */
1979         @NonNull
1980         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setAllowedAcsChannels(@andType int band, @NonNull int[] channels)1981         public Builder setAllowedAcsChannels(@BandType int band, @NonNull int[] channels) {
1982             if (!SdkLevel.isAtLeastT()) {
1983                 throw new UnsupportedOperationException();
1984             }
1985 
1986             if (channels == null) {
1987                 throw new IllegalArgumentException(
1988                         "Passing a null object to setAllowedAcsChannels");
1989             }
1990 
1991             if ((band != BAND_2GHZ) && (band != BAND_5GHZ) && (band != BAND_6GHZ)) {
1992                 throw new IllegalArgumentException(
1993                         "Passing an invalid band to setAllowedAcsChannels");
1994             }
1995 
1996             for (int channel : channels) {
1997                 if (!isChannelBandPairValid(channel, band)) {
1998                     throw new IllegalArgumentException(
1999                             "Invalid channel to setAllowedAcsChannels: band: " + band
2000                             + "channel: " + channel);
2001                 }
2002             }
2003 
2004             HashSet<Integer> set = IntStream.of(channels).boxed()
2005                     .collect(Collectors.toCollection(HashSet::new));
2006             switch(band) {
2007                 case BAND_2GHZ:
2008                     mAllowedAcsChannels2g = set;
2009                     break;
2010                 case BAND_5GHZ:
2011                     mAllowedAcsChannels5g = set;
2012                     break;
2013                 case BAND_6GHZ:
2014                     mAllowedAcsChannels6g = set;
2015                     break;
2016             }
2017 
2018             return this;
2019         }
2020 
2021         /**
2022          * Sets maximum channel bandwidth for the SoftAp Connection
2023          *
2024          * If not set, the SoftAp connection will seek the maximum channel bandwidth achievable on
2025          * the device. However, in some cases the caller will need to put a cap on the channel
2026          * bandwidth through this API.
2027          *
2028          * @param maxChannelBandwidth one of {@link SoftApInfo#CHANNEL_WIDTH_AUTO},
2029          * {@link SoftApInfo#CHANNEL_WIDTH_20MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_40MHZ},
2030          * {@link SoftApInfo#CHANNEL_WIDTH_80MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_160MHZ},
2031          * or {@link SoftApInfo#CHANNEL_WIDTH_320MHZ}
2032          *
2033          * @return builder for chaining
2034          */
2035         @NonNull
2036         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
setMaxChannelBandwidth(@ifiAnnotations.Bandwidth int maxChannelBandwidth)2037         public Builder setMaxChannelBandwidth(@WifiAnnotations.Bandwidth int maxChannelBandwidth) {
2038             if (!SdkLevel.isAtLeastT()) {
2039                 throw new UnsupportedOperationException();
2040             }
2041 
2042             switch (maxChannelBandwidth) {
2043                 case SoftApInfo.CHANNEL_WIDTH_AUTO:
2044                 case SoftApInfo.CHANNEL_WIDTH_20MHZ:
2045                 case SoftApInfo.CHANNEL_WIDTH_40MHZ:
2046                 case SoftApInfo.CHANNEL_WIDTH_80MHZ:
2047                 case SoftApInfo.CHANNEL_WIDTH_160MHZ:
2048                 case SoftApInfo.CHANNEL_WIDTH_320MHZ:
2049                     mMaxChannelBandwidth = maxChannelBandwidth;
2050                     break;
2051                 default:
2052                     throw new IllegalArgumentException(
2053                             "Invalid channel bandwidth value("
2054                             + maxChannelBandwidth + ")  configured");
2055             }
2056             return this;
2057         }
2058 
2059         /**
2060          * This method together with {@link setClientControlByUserEnabled(boolean)} control client
2061          * connections to the AP. If client control by user is disabled using the above method then
2062          * this API has no effect and clients are allowed to associate to the AP (within limit of
2063          * max number of clients).
2064          *
2065          * If client control by user is enabled then this API configures the list of clients
2066          * which are explicitly allowed. These are auto-accepted.
2067          *
2068          * All other clients which attempt to associate, whose MAC addresses are on neither list,
2069          * are:
2070          * <ul>
2071          * <li>Rejected</li>
2072          * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
2073          * is issued (which allows the user to add them to the allowed client list if desired).<li>
2074          * </ul>
2075          *
2076          * @param allowedClientList list of clients which are allowed to associate to the AP
2077          *                          without user pre-approval.
2078          * @return Builder for chaining.
2079          */
2080         @NonNull
setAllowedClientList(@onNull List<MacAddress> allowedClientList)2081         public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
2082             mAllowedClientList = new ArrayList<>(allowedClientList);
2083             return this;
2084         }
2085 
2086         /**
2087          * This API configures the list of clients which are blocked and cannot associate
2088          * to the Soft AP.
2089          *
2090          * <p>
2091          * This method requires HAL support. HAL support can be determined using
2092          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
2093          * {@link SoftApCapability#areFeaturesSupported(long)}
2094          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
2095          *
2096          * <p>
2097          * If the method is called on a device without HAL support then starting the soft AP
2098          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
2099          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
2100          *
2101          * @param blockedClientList list of clients which are not allowed to associate to the AP.
2102          * @return Builder for chaining.
2103          */
2104         @NonNull
setBlockedClientList(@onNull List<MacAddress> blockedClientList)2105         public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
2106             mBlockedClientList = new ArrayList<>(blockedClientList);
2107             return this;
2108         }
2109 
2110         /**
2111          * Specifies the level of MAC randomization for the AP BSSID.
2112          * The Soft AP BSSID will be randomized only if the BSSID isn't set
2113          * {@link #setBssid(MacAddress)} and this method is either uncalled
2114          * or called with {@link #RANDOMIZATION_PERSISTENT} or
2115          * {@link #RANDOMIZATION_NON_PERSISTENT}. When this method is called with
2116          * {@link #RANDOMIZATION_PERSISTENT} or {@link #RANDOMIZATION_NON_PERSISTENT}, the caller
2117          * the caller must not call {@link #setBssid(MacAddress)}.
2118          *
2119          * <p>
2120          * <li>If not set, defaults to {@link #RANDOMIZATION_NON_PERSISTENT}</li>
2121          *
2122          * <p>
2123          * Requires HAL support when set to {@link #RANDOMIZATION_PERSISTENT} or
2124          * {@link #RANDOMIZATION_NON_PERSISTENT}.
2125          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
2126          * {@link SoftApCapability#areFeaturesSupported(long)}
2127          * with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine
2128          * whether or not this feature is supported.
2129          *
2130          * @param macRandomizationSetting One of the following setting:
2131          * {@link #RANDOMIZATION_NONE}, {@link #RANDOMIZATION_PERSISTENT} or
2132          * {@link #RANDOMIZATION_NON_PERSISTENT}.
2133          * @return Builder for chaining.
2134          *
2135          * @see #setBssid(MacAddress)
2136          */
2137         @RequiresApi(Build.VERSION_CODES.S)
2138         @NonNull
setMacRandomizationSetting( @acRandomizationSetting int macRandomizationSetting)2139         public Builder setMacRandomizationSetting(
2140                 @MacRandomizationSetting int macRandomizationSetting) {
2141             if (!SdkLevel.isAtLeastS()) {
2142                 throw new UnsupportedOperationException();
2143             }
2144             mMacRandomizationSetting = macRandomizationSetting;
2145             return this;
2146         }
2147 
2148 
2149         /**
2150          * Specifies whether or not opportunistic shut down of an AP instance in bridged mode
2151          * is enabled.
2152          *
2153          * <p>
2154          * If enabled, the framework will shutdown one of the AP instances if it is idle for
2155          * the timeout duration - meaning there are no devices connected to it.
2156          * If both AP instances are idle for the timeout duration then the framework will
2157          * shut down the AP instance operating on the higher frequency. For instance,
2158          * if the AP instances operate at 2.4GHz and 5GHz and are both idle for the
2159          * timeout duration then the 5GHz AP instance will be shut down.
2160          * <p>
2161          *
2162          * Note: the opportunistic timeout only applies to one AP instance of the bridge AP.
2163          * If one of the AP instances has already been disabled for any reason, including due to
2164          * an opportunistic timeout or hardware issues or coexistence issues,
2165          * then the opportunistic timeout is no longer active.
2166          *
2167          * <p>
2168          * The shutdown timer specified by {@link #setShutdownTimeoutMillis(long)} controls the
2169          * overall shutdown of the bridged AP and is still in use independently of the opportunistic
2170          * timer controlled by this AP.
2171          *
2172          * <p>
2173          * <li>If not set, defaults to true</li>
2174          *
2175          * @param enable true to enable, false to disable.
2176          * @return Builder for chaining.
2177          *
2178          */
2179         @RequiresApi(Build.VERSION_CODES.S)
2180         @NonNull
setBridgedModeOpportunisticShutdownEnabled(boolean enable)2181         public Builder setBridgedModeOpportunisticShutdownEnabled(boolean enable) {
2182             if (!SdkLevel.isAtLeastS()) {
2183                 throw new UnsupportedOperationException();
2184             }
2185             mBridgedModeOpportunisticShutdownEnabled = enable;
2186             return this;
2187         }
2188 
2189         /**
2190          * Specifies whether or not to enable 802.11ax on the Soft AP.
2191          *
2192          * <p>
2193          * Note: Only relevant when the device supports 802.11ax on the Soft AP.
2194          * If enabled on devices that do not support 802.11ax then ignored.
2195          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
2196          * {@link SoftApCapability#areFeaturesSupported(long)}
2197          * with {@link SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX} to determine
2198          * whether or not 802.11ax is supported on the Soft AP.
2199          * <p>
2200          * <li>If not set, defaults to true - which will be ignored on devices
2201          * which do not support 802.11ax</li>
2202          *
2203          * @param enable true to enable, false to disable.
2204          * @return Builder for chaining.
2205          *
2206          */
2207         @RequiresApi(Build.VERSION_CODES.S)
2208         @NonNull
setIeee80211axEnabled(boolean enable)2209         public Builder setIeee80211axEnabled(boolean enable) {
2210             if (!SdkLevel.isAtLeastS()) {
2211                 throw new UnsupportedOperationException();
2212             }
2213             mIeee80211axEnabled = enable;
2214             return this;
2215         }
2216 
2217         /**
2218          * Specifies whether or not to enable 802.11be on the Soft AP.
2219          *
2220          * <p>
2221          * Note: Only relevant when the device supports 802.11be on the Soft AP.
2222          * If enabled on devices that do not support 802.11be then ignored.
2223          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
2224          * {@link SoftApCapability#areFeaturesSupported(long)}
2225          * with {@link SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE} to determine
2226          * whether or not 802.11be is supported on the Soft AP.
2227          * <p>
2228          * <li>If not set, defaults to true - which will be ignored on devices
2229          * which do not support 802.11be</li>
2230          *
2231          * @param enable true to enable, false to disable.
2232          * @return Builder for chaining.
2233          *
2234          */
2235         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
2236         @NonNull
setIeee80211beEnabled(boolean enable)2237         public Builder setIeee80211beEnabled(boolean enable) {
2238             if (!SdkLevel.isAtLeastT()) {
2239                 throw new UnsupportedOperationException();
2240             }
2241             mIeee80211beEnabled = enable;
2242             return this;
2243         }
2244 
2245         /**
2246          * Specifies whether or not the configuration is configured by user.
2247          *
2248          * @param isUserConfigured true to user configuration, false otherwise.
2249          * @return Builder for chaining.
2250          *
2251          * @hide
2252          */
2253         @NonNull
setUserConfiguration(boolean isUserConfigured)2254         public Builder setUserConfiguration(boolean isUserConfigured) {
2255             mIsUserConfiguration = isUserConfigured;
2256             return this;
2257         }
2258 
2259         /**
2260          * Specifies bridged mode opportunistic shutdown timeout in milliseconds.
2261          * An instance of bridged Soft AP will shut down when there is no device connected to it
2262          * for this timeout duration.
2263          *
2264          * Specify a value of {@link DEFAULT_TIMEOUT} to have the framework automatically use
2265          * default timeout setting defined by
2266          * {@link
2267          * R.integer.config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond}
2268          *
2269          * <p>
2270          * <li>If not set, defaults to {@link #DEFAULT_TIMEOUT}</li>
2271          * <li>The shut down timeout will apply when
2272          * {@link #setBridgedModeOpportunisticShutdownEnabled(boolean)} is set to true</li>
2273          *
2274          * @param timeoutMillis milliseconds of the timeout delay. Any value less than 1 is invalid
2275          *                      except {@link #DEFAULT_TIMEOUT}.
2276          * @return Builder for chaining.
2277          *
2278          * @see #setBridgedModeOpportunisticShutdownEnabled(boolean)
2279          */
2280         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
2281         @NonNull
setBridgedModeOpportunisticShutdownTimeoutMillis( @ntRangefrom = -1) long timeoutMillis)2282         public Builder setBridgedModeOpportunisticShutdownTimeoutMillis(
2283                 @IntRange(from = -1) long timeoutMillis) {
2284             if (!SdkLevel.isAtLeastT()) {
2285                 throw new UnsupportedOperationException();
2286             }
2287             if (timeoutMillis < 1 && timeoutMillis != DEFAULT_TIMEOUT) {
2288                 throw new IllegalArgumentException("Invalid timeout value: " + timeoutMillis);
2289             }
2290             mBridgedModeOpportunisticShutdownTimeoutMillis = timeoutMillis;
2291             return this;
2292         }
2293 
2294         /**
2295          * @param mac persistent randomized MacAddress generated by the frameworks.
2296          * @hide
2297          */
2298         @NonNull
setRandomizedMacAddress(@onNull MacAddress mac)2299         public Builder setRandomizedMacAddress(@NonNull MacAddress mac) {
2300             if (mac == null) {
2301                 throw new IllegalArgumentException("setRandomizedMacAddress received"
2302                         + " null MacAddress.");
2303             }
2304             mPersistentRandomizedMacAddress = mac;
2305             return this;
2306         }
2307 
2308         /**
2309          * Set additional vendor-provided configuration data.
2310          *
2311          * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided
2312          *     configuration data. Note that multiple elements with the same OUI are allowed.
2313          * @return Builder for chaining.
2314          */
2315         @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
2316         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
2317         @NonNull
setVendorData(@onNull List<OuiKeyedData> vendorData)2318         public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) {
2319             if (!SdkLevel.isAtLeastV()) {
2320                 throw new UnsupportedOperationException();
2321             }
2322             if (vendorData == null) {
2323                 throw new IllegalArgumentException("setVendorData received a null value");
2324             }
2325             mVendorData = vendorData;
2326             return this;
2327         }
2328     }
2329 }
2330