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.net.MacAddress;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.util.Preconditions;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.nio.charset.CharsetEncoder;
36 import java.nio.charset.StandardCharsets;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Objects;
40 
41 /**
42  * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
43  *
44  * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
45  * framework how it should configure a hotspot.
46  *
47  * System apps can use this to configure a tethered hotspot using
48  * {@code WifiManager#startTetheredHotspot(SoftApConfiguration)} and
49  * {@code WifiManager#setSoftApConfiguration(SoftApConfiguration)}
50  * or local-only hotspot using
51  * {@code WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
52  * WifiManager.LocalOnlyHotspotCallback)}.
53  *
54  * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to
55  * create a new instance.
56  *
57  */
58 public final class SoftApConfiguration implements Parcelable {
59 
60     private static final String TAG = "SoftApConfiguration";
61 
62     @VisibleForTesting
63     static final int PSK_MIN_LEN = 8;
64 
65     @VisibleForTesting
66     static final int PSK_MAX_LEN = 63;
67 
68     /**
69      * 2GHz band.
70      * @hide
71      */
72     @SystemApi
73     public static final int BAND_2GHZ = 1 << 0;
74 
75     /**
76      * 5GHz band.
77      * @hide
78      */
79     @SystemApi
80     public static final int BAND_5GHZ = 1 << 1;
81 
82     /**
83      * 6GHz band.
84      * @hide
85      */
86     @SystemApi
87     public static final int BAND_6GHZ = 1 << 2;
88 
89     /**
90      * Device is allowed to choose the optimal band (2Ghz, 5Ghz, 6Ghz) based on device capability,
91      * operating country code and current radio conditions.
92      * @hide
93      */
94     @SystemApi
95     public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;
96 
97     /** @hide */
98     @Retention(RetentionPolicy.SOURCE)
99     @IntDef(flag = true, prefix = { "BAND_TYPE_" }, value = {
100             BAND_2GHZ,
101             BAND_5GHZ,
102             BAND_6GHZ,
103     })
104     public @interface BandType {}
105 
isBandValid(@andType int band)106     private static boolean isBandValid(@BandType int band) {
107         return ((band != 0) && ((band & ~BAND_ANY) == 0));
108     }
109 
110     private static final int MIN_CH_2G_BAND = 1;
111     private static final int MAX_CH_2G_BAND = 14;
112     private static final int MIN_CH_5G_BAND = 34;
113     private static final int MAX_CH_5G_BAND = 196;
114     private static final int MIN_CH_6G_BAND = 1;
115     private static final int MAX_CH_6G_BAND = 253;
116 
117 
118 
isChannelBandPairValid(int channel, @BandType int band)119     private static boolean isChannelBandPairValid(int channel, @BandType int band) {
120         switch (band) {
121             case BAND_2GHZ:
122                 if (channel < MIN_CH_2G_BAND || channel >  MAX_CH_2G_BAND) {
123                     return false;
124                 }
125                 break;
126 
127             case BAND_5GHZ:
128                 if (channel < MIN_CH_5G_BAND || channel >  MAX_CH_5G_BAND) {
129                     return false;
130                 }
131                 break;
132 
133             case BAND_6GHZ:
134                 if (channel < MIN_CH_6G_BAND || channel >  MAX_CH_6G_BAND) {
135                     return false;
136                 }
137                 break;
138             default:
139                 return false;
140         }
141         return true;
142     }
143 
144     /**
145      * SSID for the AP, or null for a framework-determined SSID.
146      */
147     private final @Nullable String mSsid;
148 
149     /**
150      * BSSID for the AP, or null to use a framework-determined BSSID.
151      */
152     private final @Nullable MacAddress mBssid;
153 
154     /**
155      * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on
156      * the security type.
157      */
158     private final @Nullable String mPassphrase;
159 
160     /**
161      * This is a network that does not broadcast its SSID, so an
162      * SSID-specific probe request must be used for scans.
163      */
164     private final boolean mHiddenSsid;
165 
166     /**
167      * The operating band of the AP.
168      * One or combination of the following band type:
169      * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
170      */
171     private final @BandType int mBand;
172 
173     /**
174      * The operating channel of the AP.
175      */
176     private final int mChannel;
177 
178     /**
179      * The maximim allowed number of clients that can associate to the AP.
180      */
181     private final int mMaxNumberOfClients;
182 
183     /**
184      * The operating security type of the AP.
185      * One of the following security types:
186      * {@link #SECURITY_TYPE_OPEN},
187      * {@link #SECURITY_TYPE_WPA2_PSK},
188      * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
189      * {@link #SECURITY_TYPE_WPA3_SAE}
190      */
191     private final @SecurityType int mSecurityType;
192 
193     /**
194      * The flag to indicate client need to authorize by user
195      * when client is connecting to AP.
196      */
197     private final boolean mClientControlByUser;
198 
199     /**
200      * The list of blocked client that can't associate to the AP.
201      */
202     private final List<MacAddress> mBlockedClientList;
203 
204     /**
205      * The list of allowed client that can associate to the AP.
206      */
207     private final List<MacAddress> mAllowedClientList;
208 
209     /**
210      * Whether auto shutdown of soft AP is enabled or not.
211      */
212     private final boolean mAutoShutdownEnabled;
213 
214     /**
215      * Delay in milliseconds before shutting down soft AP when
216      * there are no connected devices.
217      */
218     private final long mShutdownTimeoutMillis;
219 
220     /**
221      * THe definition of security type OPEN.
222      */
223     public static final int SECURITY_TYPE_OPEN = 0;
224 
225     /**
226      * The definition of security type WPA2-PSK.
227      */
228     public static final int SECURITY_TYPE_WPA2_PSK = 1;
229 
230     /**
231      * The definition of security type WPA3-SAE Transition mode.
232      */
233     public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2;
234 
235     /**
236      * The definition of security type WPA3-SAE.
237      */
238     public static final int SECURITY_TYPE_WPA3_SAE = 3;
239 
240     /** @hide */
241     @Retention(RetentionPolicy.SOURCE)
242     @IntDef(prefix = { "SECURITY_TYPE_" }, value = {
243         SECURITY_TYPE_OPEN,
244         SECURITY_TYPE_WPA2_PSK,
245         SECURITY_TYPE_WPA3_SAE_TRANSITION,
246         SECURITY_TYPE_WPA3_SAE,
247     })
248     public @interface SecurityType {}
249 
250     /** Private constructor for Builder and Parcelable implementation. */
SoftApConfiguration(@ullable String ssid, @Nullable MacAddress bssid, @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel, @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, long shutdownTimeoutMillis, boolean clientControlByUser, @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList)251     private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
252             @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
253             @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
254             long shutdownTimeoutMillis, boolean clientControlByUser,
255             @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
256         mSsid = ssid;
257         mBssid = bssid;
258         mPassphrase = passphrase;
259         mHiddenSsid = hiddenSsid;
260         mBand = band;
261         mChannel = channel;
262         mSecurityType = securityType;
263         mMaxNumberOfClients = maxNumberOfClients;
264         mAutoShutdownEnabled = shutdownTimeoutEnabled;
265         mShutdownTimeoutMillis = shutdownTimeoutMillis;
266         mClientControlByUser = clientControlByUser;
267         mBlockedClientList = new ArrayList<>(blockedList);
268         mAllowedClientList = new ArrayList<>(allowedList);
269     }
270 
271     @Override
equals(Object otherObj)272     public boolean equals(Object otherObj) {
273         if (this == otherObj) {
274             return true;
275         }
276         if (!(otherObj instanceof SoftApConfiguration)) {
277             return false;
278         }
279         SoftApConfiguration other = (SoftApConfiguration) otherObj;
280         return Objects.equals(mSsid, other.mSsid)
281                 && Objects.equals(mBssid, other.mBssid)
282                 && Objects.equals(mPassphrase, other.mPassphrase)
283                 && mHiddenSsid == other.mHiddenSsid
284                 && mBand == other.mBand
285                 && mChannel == other.mChannel
286                 && mSecurityType == other.mSecurityType
287                 && mMaxNumberOfClients == other.mMaxNumberOfClients
288                 && mAutoShutdownEnabled == other.mAutoShutdownEnabled
289                 && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
290                 && mClientControlByUser == other.mClientControlByUser
291                 && Objects.equals(mBlockedClientList, other.mBlockedClientList)
292                 && Objects.equals(mAllowedClientList, other.mAllowedClientList);
293     }
294 
295     @Override
hashCode()296     public int hashCode() {
297         return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
298                 mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
299                 mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
300                 mAllowedClientList);
301     }
302 
303     @Override
toString()304     public String toString() {
305         StringBuilder sbuf = new StringBuilder();
306         sbuf.append("ssid=").append(mSsid);
307         if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString());
308         sbuf.append(" \n Passphrase =").append(
309                 TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>");
310         sbuf.append(" \n HiddenSsid =").append(mHiddenSsid);
311         sbuf.append(" \n Band =").append(mBand);
312         sbuf.append(" \n Channel =").append(mChannel);
313         sbuf.append(" \n SecurityType=").append(getSecurityType());
314         sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
315         sbuf.append(" \n AutoShutdownEnabled=").append(mAutoShutdownEnabled);
316         sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
317         sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
318         sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
319         sbuf.append(" \n AllowedClientList=").append(mAllowedClientList);
320         return sbuf.toString();
321     }
322 
323     @Override
writeToParcel(@onNull Parcel dest, int flags)324     public void writeToParcel(@NonNull Parcel dest, int flags) {
325         dest.writeString(mSsid);
326         dest.writeParcelable(mBssid, flags);
327         dest.writeString(mPassphrase);
328         dest.writeBoolean(mHiddenSsid);
329         dest.writeInt(mBand);
330         dest.writeInt(mChannel);
331         dest.writeInt(mSecurityType);
332         dest.writeInt(mMaxNumberOfClients);
333         dest.writeBoolean(mAutoShutdownEnabled);
334         dest.writeLong(mShutdownTimeoutMillis);
335         dest.writeBoolean(mClientControlByUser);
336         dest.writeTypedList(mBlockedClientList);
337         dest.writeTypedList(mAllowedClientList);
338     }
339 
340     @Override
describeContents()341     public int describeContents() {
342         return 0;
343     }
344 
345     @NonNull
346     public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() {
347         @Override
348         public SoftApConfiguration createFromParcel(Parcel in) {
349             return new SoftApConfiguration(
350                     in.readString(),
351                     in.readParcelable(MacAddress.class.getClassLoader()),
352                     in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
353                     in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(),
354                     in.createTypedArrayList(MacAddress.CREATOR),
355                     in.createTypedArrayList(MacAddress.CREATOR));
356         }
357 
358         @Override
359         public SoftApConfiguration[] newArray(int size) {
360             return new SoftApConfiguration[size];
361         }
362     };
363 
364     /**
365      * Return String set to be the SSID for the AP.
366      * {@link Builder#setSsid(String)}.
367      */
368     @Nullable
getSsid()369     public String getSsid() {
370         return mSsid;
371     }
372 
373     /**
374      * Returns MAC address set to be BSSID for the AP.
375      * {@link Builder#setBssid(MacAddress)}.
376      */
377     @Nullable
getBssid()378     public MacAddress getBssid() {
379         return mBssid;
380     }
381 
382     /**
383      * Returns String set to be passphrase for current AP.
384      * {@link Builder#setPassphrase(String, int)}.
385      */
386     @Nullable
getPassphrase()387     public String getPassphrase() {
388         return mPassphrase;
389     }
390 
391     /**
392      * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
393      * not (false: broadcasts its SSID) for the AP.
394      * {@link Builder#setHiddenSsid(boolean)}.
395      */
isHiddenSsid()396     public boolean isHiddenSsid() {
397         return mHiddenSsid;
398     }
399 
400     /**
401      * Returns band type set to be the band for the AP.
402      *
403      * One or combination of the following band type:
404      * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
405      *
406      * {@link Builder#setBand(int)}.
407      *
408      * @hide
409      */
410     @SystemApi
getBand()411     public @BandType int getBand() {
412         return mBand;
413     }
414 
415     /**
416      * Returns Integer set to be the channel for the AP.
417      * {@link Builder#setChannel(int)}.
418      *
419      * @hide
420      */
421     @SystemApi
getChannel()422     public int getChannel() {
423         return mChannel;
424     }
425 
426     /**
427      * Get security type params which depends on which security passphrase to set.
428      *
429      * @return One of:
430      * {@link #SECURITY_TYPE_OPEN},
431      * {@link #SECURITY_TYPE_WPA2_PSK},
432      * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
433      * {@link #SECURITY_TYPE_WPA3_SAE}
434      */
getSecurityType()435     public @SecurityType int getSecurityType() {
436         return mSecurityType;
437     }
438 
439     /**
440      * Returns the maximum number of clients that can associate to the AP.
441      * {@link Builder#setMaxNumberOfClients(int)}.
442      *
443      * @hide
444      */
445     @SystemApi
getMaxNumberOfClients()446     public int getMaxNumberOfClients() {
447         return mMaxNumberOfClients;
448     }
449 
450     /**
451      * Returns whether auto shutdown is enabled or not.
452      * The Soft AP will shutdown when there are no devices associated to it for
453      * the timeout duration. See {@link Builder#setAutoShutdownEnabled(boolean)}.
454      *
455      * @hide
456      */
457     @SystemApi
isAutoShutdownEnabled()458     public boolean isAutoShutdownEnabled() {
459         return mAutoShutdownEnabled;
460     }
461 
462     /**
463      * Returns the shutdown timeout in milliseconds.
464      * The Soft AP will shutdown when there are no devices associated to it for
465      * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(long)}.
466      *
467      * @hide
468      */
469     @SystemApi
getShutdownTimeoutMillis()470     public long getShutdownTimeoutMillis() {
471         return mShutdownTimeoutMillis;
472     }
473 
474     /**
475      * Returns a flag indicating whether clients need to be pre-approved by the user.
476      * (true: authorization required) or not (false: not required).
477      * {@link Builder#setClientControlByUserEnabled(Boolean)}.
478      *
479      * @hide
480      */
481     @SystemApi
isClientControlByUserEnabled()482     public boolean isClientControlByUserEnabled() {
483         return mClientControlByUser;
484     }
485 
486     /**
487      * Returns List of clients which aren't allowed to associate to the AP.
488      *
489      * Clients are configured using {@link Builder#setBlockedClientList(List)}
490      *
491      * @hide
492      */
493     @NonNull
494     @SystemApi
getBlockedClientList()495     public List<MacAddress> getBlockedClientList() {
496         return mBlockedClientList;
497     }
498 
499     /**
500      * List of clients which are allowed to associate to the AP.
501      * Clients are configured using {@link Builder#setAllowedClientList(List)}
502      *
503      * @hide
504      */
505     @NonNull
506     @SystemApi
getAllowedClientList()507     public List<MacAddress> getAllowedClientList() {
508         return mAllowedClientList;
509     }
510 
511     /**
512      * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}.
513      * Note that SoftApConfiguration may contain configuration which is cannot be represented
514      * by the legacy WifiConfiguration, in such cases a null will be returned.
515      *
516      * <li> SoftAp band in {@link WifiConfiguration.apBand} only supports
517      * 2GHz, 5GHz, 2GHz+5GHz bands, so conversion is limited to these bands. </li>
518      *
519      * <li> SoftAp security type in {@link WifiConfiguration.KeyMgmt} only supports
520      * NONE, WPA2_PSK, so conversion is limited to these security type.</li>
521      * @hide
522      */
523     @Nullable
524     @SystemApi
toWifiConfiguration()525     public WifiConfiguration toWifiConfiguration() {
526         WifiConfiguration wifiConfig = new WifiConfiguration();
527         wifiConfig.SSID = mSsid;
528         wifiConfig.preSharedKey = mPassphrase;
529         wifiConfig.hiddenSSID = mHiddenSsid;
530         wifiConfig.apChannel = mChannel;
531         switch (mSecurityType) {
532             case SECURITY_TYPE_OPEN:
533                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
534                 break;
535             case SECURITY_TYPE_WPA2_PSK:
536                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
537                 break;
538             default:
539                 Log.e(TAG, "Convert fail, unsupported security type :" + mSecurityType);
540                 return null;
541         }
542 
543         switch (mBand) {
544             case BAND_2GHZ:
545                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_2GHZ;
546                 break;
547             case BAND_5GHZ:
548                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_5GHZ;
549                 break;
550             case BAND_2GHZ | BAND_5GHZ:
551                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
552                 break;
553             case BAND_ANY:
554                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
555                 break;
556             default:
557                 Log.e(TAG, "Convert fail, unsupported band setting :" + mBand);
558                 return null;
559         }
560         return wifiConfig;
561     }
562 
563     /**
564      * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
565      * Soft AP.
566      *
567      * All fields are optional. By default, SSID and BSSID are automatically chosen by the
568      * framework, and an open network is created.
569      *
570      * @hide
571      */
572     @SystemApi
573     public static final class Builder {
574         private String mSsid;
575         private MacAddress mBssid;
576         private String mPassphrase;
577         private boolean mHiddenSsid;
578         private int mBand;
579         private int mChannel;
580         private int mMaxNumberOfClients;
581         private int mSecurityType;
582         private boolean mAutoShutdownEnabled;
583         private long mShutdownTimeoutMillis;
584         private boolean mClientControlByUser;
585         private List<MacAddress> mBlockedClientList;
586         private List<MacAddress> mAllowedClientList;
587 
588         /**
589          * Constructs a Builder with default values (see {@link Builder}).
590          */
Builder()591         public Builder() {
592             mSsid = null;
593             mBssid = null;
594             mPassphrase = null;
595             mHiddenSsid = false;
596             mBand = BAND_2GHZ;
597             mChannel = 0;
598             mMaxNumberOfClients = 0;
599             mSecurityType = SECURITY_TYPE_OPEN;
600             mAutoShutdownEnabled = true; // enabled by default.
601             mShutdownTimeoutMillis = 0;
602             mClientControlByUser = false;
603             mBlockedClientList = new ArrayList<>();
604             mAllowedClientList = new ArrayList<>();
605         }
606 
607         /**
608          * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
609          */
Builder(@onNull SoftApConfiguration other)610         public Builder(@NonNull SoftApConfiguration other) {
611             Objects.requireNonNull(other);
612 
613             mSsid = other.mSsid;
614             mBssid = other.mBssid;
615             mPassphrase = other.mPassphrase;
616             mHiddenSsid = other.mHiddenSsid;
617             mBand = other.mBand;
618             mChannel = other.mChannel;
619             mMaxNumberOfClients = other.mMaxNumberOfClients;
620             mSecurityType = other.mSecurityType;
621             mAutoShutdownEnabled = other.mAutoShutdownEnabled;
622             mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
623             mClientControlByUser = other.mClientControlByUser;
624             mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
625             mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
626         }
627 
628         /**
629          * Builds the {@link SoftApConfiguration}.
630          *
631          * @return A new {@link SoftApConfiguration}, as configured by previous method calls.
632          */
633         @NonNull
build()634         public SoftApConfiguration build() {
635             for (MacAddress client : mAllowedClientList) {
636                 if (mBlockedClientList.contains(client)) {
637                     throw new IllegalArgumentException("A MacAddress exist in both client list");
638                 }
639             }
640             return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
641                     mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
642                     mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
643                     mBlockedClientList, mAllowedClientList);
644         }
645 
646         /**
647          * Specifies an SSID for the AP.
648          * <p>
649          * Null SSID only support when configure a local-only hotspot.
650          * <p>
651          * <li>If not set, defaults to null.</li>
652          *
653          * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
654          *             chosen by the framework.
655          * @return Builder for chaining.
656          * @throws IllegalArgumentException when the SSID is empty or not valid Unicode.
657          */
658         @NonNull
setSsid(@ullable String ssid)659         public Builder setSsid(@Nullable String ssid) {
660             if (ssid != null) {
661                 Preconditions.checkStringNotEmpty(ssid);
662                 Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
663             }
664             mSsid = ssid;
665             return this;
666         }
667 
668         /**
669          * Specifies a BSSID for the AP.
670          * <p>
671          * <li>If not set, defaults to null.</li>
672          * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
673          *              responsible for avoiding collisions.
674          * @return Builder for chaining.
675          * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC
676          *                                  address.
677          */
678         @NonNull
setBssid(@ullable MacAddress bssid)679         public Builder setBssid(@Nullable MacAddress bssid) {
680             if (bssid != null) {
681                 Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
682                 Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
683             }
684             mBssid = bssid;
685             return this;
686         }
687 
688         /**
689          * Specifies that this AP should use specific security type with the given ASCII passphrase.
690          *
691          * @param securityType One of the following security types:
692          * {@link #SECURITY_TYPE_OPEN},
693          * {@link #SECURITY_TYPE_WPA2_PSK},
694          * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
695          * {@link #SECURITY_TYPE_WPA3_SAE}.
696          * @param passphrase The passphrase to use for sepcific {@code securityType} configuration
697          * or null with {@link #SECURITY_TYPE_OPEN}.
698          *
699          * @return Builder for chaining.
700          * @throws IllegalArgumentException when the passphrase length is invalid and
701          *         {@code securityType} is not {@link #SECURITY_TYPE_OPEN}
702          *         or non-null passphrase and {@code securityType} is
703          *         {@link #SECURITY_TYPE_OPEN}.
704          */
705         @NonNull
setPassphrase(@ullable String passphrase, @SecurityType int securityType)706         public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
707             if (securityType == SECURITY_TYPE_OPEN) {
708                 if (passphrase != null) {
709                     throw new IllegalArgumentException(
710                             "passphrase should be null when security type is open");
711                 }
712             } else {
713                 Preconditions.checkStringNotEmpty(passphrase);
714                 final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
715                 if (!asciiEncoder.canEncode(passphrase)) {
716                     throw new IllegalArgumentException("passphrase not ASCII encodable");
717                 }
718                 if (securityType == SECURITY_TYPE_WPA2_PSK
719                         || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
720                     if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
721                         throw new IllegalArgumentException(
722                                 "Password size must be at least " + PSK_MIN_LEN
723                                 + " and no more than " + PSK_MAX_LEN
724                                 + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
725                     }
726                 }
727             }
728             mSecurityType = securityType;
729             mPassphrase = passphrase;
730             return this;
731         }
732 
733         /**
734          * Specifies whether the AP is hidden (doesn't broadcast its SSID) or
735          * not (broadcasts its SSID).
736          * <p>
737          * <li>If not set, defaults to false (i.e not a hidden network).</li>
738          *
739          * @param hiddenSsid true for a hidden SSID, false otherwise.
740          * @return Builder for chaining.
741          */
742         @NonNull
setHiddenSsid(boolean hiddenSsid)743         public Builder setHiddenSsid(boolean hiddenSsid) {
744             mHiddenSsid = hiddenSsid;
745             return this;
746         }
747 
748         /**
749          * Specifies the band for the AP.
750          * <p>
751          * <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
752          *
753          * @param band One or combination of the following band type:
754          * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
755          * @return Builder for chaining.
756          */
757         @NonNull
setBand(@andType int band)758         public Builder setBand(@BandType int band) {
759             if (!isBandValid(band)) {
760                 throw new IllegalArgumentException("Invalid band type");
761             }
762             mBand = band;
763             // Since band preference is specified, no specific channel is selected.
764             mChannel = 0;
765             return this;
766         }
767 
768         /**
769          * Specifies the channel and associated band for the AP.
770          *
771          * The channel which AP resides on. Valid channels are country dependent.
772          * <p>
773          * The default for the channel is a the special value 0 to have the framework
774          * auto-select a valid channel from the band configured with
775          * {@link #setBand(int)}.
776          *
777          * The channel auto selection will offload to driver when
778          * {@link SoftApCapability#areFeaturesSupported(
779          * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
780          * return true. Driver will auto select best channel which based on environment
781          * interference to get best performance. Check {@link SoftApCapability} to get more detail.
782          *
783          * Note, since 6GHz band use the same channel numbering of 2.4GHz and 5GHZ bands,
784          * the caller needs to pass the band containing the selected channel.
785          *
786          * <p>
787          * <li>If not set, defaults to 0.</li>
788          * @param channel operating channel of the AP.
789          * @param band containing this channel.
790          * @return Builder for chaining.
791          */
792         @NonNull
setChannel(int channel, @BandType int band)793         public Builder setChannel(int channel, @BandType int band) {
794             if (!isChannelBandPairValid(channel, band)) {
795                 throw new IllegalArgumentException("Invalid band type");
796             }
797             mBand = band;
798             mChannel = channel;
799             return this;
800         }
801 
802         /**
803          * Specifies the maximum number of clients that can associate to the AP.
804          *
805          * The maximum number of clients (STAs) which can associate to the AP.
806          * The AP will reject association from any clients above this number.
807          * Specify a value of 0 to have the framework automatically use the maximum number
808          * which the device can support (based on hardware and carrier constraints).
809          * <p>
810          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
811          * {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients
812          * which the device supports (based on hardware and carrier constraints).
813          *
814          * <p>
815          * <li>If not set, defaults to 0.</li>
816          *
817          * This method requires hardware support. If the method is used to set a
818          * non-zero {@code maxNumberOfClients} value then
819          * {@link WifiManager#startTetheredHotspot} will report error code
820          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
821          *
822          * <p>
823          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
824          * {@link SoftApCapability#areFeaturesSupported(int)}
825          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
826          * or not this feature is supported.
827          *
828          * @param maxNumberOfClients maximum client number of the AP.
829          * @return Builder for chaining.
830          */
831         @NonNull
setMaxNumberOfClients(@ntRangefrom = 0) int maxNumberOfClients)832         public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
833             if (maxNumberOfClients < 0) {
834                 throw new IllegalArgumentException("maxNumberOfClients should be not negative");
835             }
836             mMaxNumberOfClients = maxNumberOfClients;
837             return this;
838         }
839 
840         /**
841          * Specifies whether auto shutdown is enabled or not.
842          * The Soft AP will shut down when there are no devices connected to it for
843          * the timeout duration.
844          *
845          * <p>
846          * <li>If not set, defaults to true</li>
847          *
848          * @param enable true to enable, false to disable.
849          * @return Builder for chaining.
850          *
851          * @see #setShutdownTimeoutMillis(long)
852          */
853         @NonNull
setAutoShutdownEnabled(boolean enable)854         public Builder setAutoShutdownEnabled(boolean enable) {
855             mAutoShutdownEnabled = enable;
856             return this;
857         }
858 
859         /**
860          * Specifies the shutdown timeout in milliseconds.
861          * The Soft AP will shut down when there are no devices connected to it for
862          * the timeout duration.
863          *
864          * Specify a value of 0 to have the framework automatically use default timeout
865          * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
866          *
867          * <p>
868          * <li>If not set, defaults to 0</li>
869          * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
870          * set to true</li>
871          *
872          * @param timeoutMillis milliseconds of the timeout delay.
873          * @return Builder for chaining.
874          *
875          * @see #setAutoShutdownEnabled(boolean)
876          */
877         @NonNull
setShutdownTimeoutMillis(@ntRangefrom = 0) long timeoutMillis)878         public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) {
879             if (timeoutMillis < 0) {
880                 throw new IllegalArgumentException("Invalid timeout value");
881             }
882             mShutdownTimeoutMillis = timeoutMillis;
883             return this;
884         }
885 
886         /**
887          * Configure the Soft AP to require manual user control of client association.
888          * If disabled (the default) then any client which isn't in the blocked list
889          * {@link #getBlockedClientList()} can associate to this Soft AP using the
890          * correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier,
891          * or user limited - using {@link #setMaxNumberOfClients(int)}).
892          *
893          * If manual user control is enabled then clients will be accepted, rejected, or require
894          * a user approval based on the configuration provided by
895          * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
896          *
897          * <p>
898          * This method requires hardware support. Hardware support can be determined using
899          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
900          * {@link SoftApCapability#areFeaturesSupported(int)}
901          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
902          *
903          * <p>
904          * If the method is called on a device without hardware support then starting the soft AP
905          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
906          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
907          *
908          * <p>
909          * <li>If not set, defaults to false (i.e The authoriztion is not required).</li>
910          *
911          * @param enabled true for enabling the control by user, false otherwise.
912          * @return Builder for chaining.
913          */
914         @NonNull
setClientControlByUserEnabled(boolean enabled)915         public Builder setClientControlByUserEnabled(boolean enabled) {
916             mClientControlByUser = enabled;
917             return this;
918         }
919 
920 
921         /**
922          * This method together with {@link setClientControlByUserEnabled(boolean)} control client
923          * connections to the AP. If client control by user is disabled using the above method then
924          * this API has no effect and clients are allowed to associate to the AP (within limit of
925          * max number of clients).
926          *
927          * If client control by user is enabled then this API configures the list of clients
928          * which are explicitly allowed. These are auto-accepted.
929          *
930          * All other clients which attempt to associate, whose MAC addresses are on neither list,
931          * are:
932          * <ul>
933          * <li>Rejected</li>
934          * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
935          * is issued (which allows the user to add them to the allowed client list if desired).<li>
936          * </ul>
937          *
938          * @param allowedClientList list of clients which are allowed to associate to the AP
939          *                          without user pre-approval.
940          * @return Builder for chaining.
941          */
942         @NonNull
setAllowedClientList(@onNull List<MacAddress> allowedClientList)943         public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
944             mAllowedClientList = new ArrayList<>(allowedClientList);
945             return this;
946         }
947 
948         /**
949          * This API configures the list of clients which are blocked and cannot associate
950          * to the Soft AP.
951          *
952          * <p>
953          * This method requires hardware support. Hardware support can be determined using
954          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
955          * {@link SoftApCapability#areFeaturesSupported(int)}
956          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
957          *
958          * <p>
959          * If the method is called on a device without hardware support then starting the soft AP
960          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
961          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
962          *
963          * @param blockedClientList list of clients which are not allowed to associate to the AP.
964          * @return Builder for chaining.
965          */
966         @NonNull
setBlockedClientList(@onNull List<MacAddress> blockedClientList)967         public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
968             mBlockedClientList = new ArrayList<>(blockedClientList);
969             return this;
970         }
971     }
972 }
973