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