• 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.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             case SECURITY_TYPE_WPA3_SAE_TRANSITION:
537                 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
538                 break;
539             default:
540                 Log.e(TAG, "Convert fail, unsupported security type :" + mSecurityType);
541                 return null;
542         }
543 
544         switch (mBand) {
545             case BAND_2GHZ:
546                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_2GHZ;
547                 break;
548             case BAND_5GHZ:
549                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_5GHZ;
550                 break;
551             case BAND_2GHZ | BAND_5GHZ:
552                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
553                 break;
554             case BAND_ANY:
555                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
556                 break;
557             default:
558                 Log.e(TAG, "Convert fail, unsupported band setting :" + mBand);
559                 return null;
560         }
561         return wifiConfig;
562     }
563 
564     /**
565      * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
566      * Soft AP.
567      *
568      * All fields are optional. By default, SSID and BSSID are automatically chosen by the
569      * framework, and an open network is created.
570      *
571      * @hide
572      */
573     @SystemApi
574     public static final class Builder {
575         private String mSsid;
576         private MacAddress mBssid;
577         private String mPassphrase;
578         private boolean mHiddenSsid;
579         private int mBand;
580         private int mChannel;
581         private int mMaxNumberOfClients;
582         private int mSecurityType;
583         private boolean mAutoShutdownEnabled;
584         private long mShutdownTimeoutMillis;
585         private boolean mClientControlByUser;
586         private List<MacAddress> mBlockedClientList;
587         private List<MacAddress> mAllowedClientList;
588 
589         /**
590          * Constructs a Builder with default values (see {@link Builder}).
591          */
Builder()592         public Builder() {
593             mSsid = null;
594             mBssid = null;
595             mPassphrase = null;
596             mHiddenSsid = false;
597             mBand = BAND_2GHZ;
598             mChannel = 0;
599             mMaxNumberOfClients = 0;
600             mSecurityType = SECURITY_TYPE_OPEN;
601             mAutoShutdownEnabled = true; // enabled by default.
602             mShutdownTimeoutMillis = 0;
603             mClientControlByUser = false;
604             mBlockedClientList = new ArrayList<>();
605             mAllowedClientList = new ArrayList<>();
606         }
607 
608         /**
609          * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
610          */
Builder(@onNull SoftApConfiguration other)611         public Builder(@NonNull SoftApConfiguration other) {
612             Objects.requireNonNull(other);
613 
614             mSsid = other.mSsid;
615             mBssid = other.mBssid;
616             mPassphrase = other.mPassphrase;
617             mHiddenSsid = other.mHiddenSsid;
618             mBand = other.mBand;
619             mChannel = other.mChannel;
620             mMaxNumberOfClients = other.mMaxNumberOfClients;
621             mSecurityType = other.mSecurityType;
622             mAutoShutdownEnabled = other.mAutoShutdownEnabled;
623             mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
624             mClientControlByUser = other.mClientControlByUser;
625             mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
626             mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
627         }
628 
629         /**
630          * Builds the {@link SoftApConfiguration}.
631          *
632          * @return A new {@link SoftApConfiguration}, as configured by previous method calls.
633          */
634         @NonNull
build()635         public SoftApConfiguration build() {
636             for (MacAddress client : mAllowedClientList) {
637                 if (mBlockedClientList.contains(client)) {
638                     throw new IllegalArgumentException("A MacAddress exist in both client list");
639                 }
640             }
641             return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
642                     mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
643                     mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
644                     mBlockedClientList, mAllowedClientList);
645         }
646 
647         /**
648          * Specifies an SSID for the AP.
649          * <p>
650          * Null SSID only support when configure a local-only hotspot.
651          * <p>
652          * <li>If not set, defaults to null.</li>
653          *
654          * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
655          *             chosen by the framework.
656          * @return Builder for chaining.
657          * @throws IllegalArgumentException when the SSID is empty or not valid Unicode.
658          */
659         @NonNull
setSsid(@ullable String ssid)660         public Builder setSsid(@Nullable String ssid) {
661             if (ssid != null) {
662                 Preconditions.checkStringNotEmpty(ssid);
663                 Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
664             }
665             mSsid = ssid;
666             return this;
667         }
668 
669         /**
670          * Specifies a BSSID for the AP.
671          * <p>
672          * <li>If not set, defaults to null.</li>
673          * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
674          *              responsible for avoiding collisions.
675          * @return Builder for chaining.
676          * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC
677          *                                  address.
678          */
679         @NonNull
setBssid(@ullable MacAddress bssid)680         public Builder setBssid(@Nullable MacAddress bssid) {
681             if (bssid != null) {
682                 Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
683                 Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
684             }
685             mBssid = bssid;
686             return this;
687         }
688 
689         /**
690          * Specifies that this AP should use specific security type with the given ASCII passphrase.
691          *
692          * @param securityType One of the following security types:
693          * {@link #SECURITY_TYPE_OPEN},
694          * {@link #SECURITY_TYPE_WPA2_PSK},
695          * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
696          * {@link #SECURITY_TYPE_WPA3_SAE}.
697          * @param passphrase The passphrase to use for sepcific {@code securityType} configuration
698          * or null with {@link #SECURITY_TYPE_OPEN}.
699          *
700          * @return Builder for chaining.
701          * @throws IllegalArgumentException when the passphrase length is invalid and
702          *         {@code securityType} is not {@link #SECURITY_TYPE_OPEN}
703          *         or non-null passphrase and {@code securityType} is
704          *         {@link #SECURITY_TYPE_OPEN}.
705          */
706         @NonNull
setPassphrase(@ullable String passphrase, @SecurityType int securityType)707         public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
708             if (securityType == SECURITY_TYPE_OPEN) {
709                 if (passphrase != null) {
710                     throw new IllegalArgumentException(
711                             "passphrase should be null when security type is open");
712                 }
713             } else {
714                 Preconditions.checkStringNotEmpty(passphrase);
715                 final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
716                 if (!asciiEncoder.canEncode(passphrase)) {
717                     throw new IllegalArgumentException("passphrase not ASCII encodable");
718                 }
719                 if (securityType == SECURITY_TYPE_WPA2_PSK
720                         || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
721                     if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
722                         throw new IllegalArgumentException(
723                                 "Password size must be at least " + PSK_MIN_LEN
724                                 + " and no more than " + PSK_MAX_LEN
725                                 + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
726                     }
727                 }
728             }
729             mSecurityType = securityType;
730             mPassphrase = passphrase;
731             return this;
732         }
733 
734         /**
735          * Specifies whether the AP is hidden (doesn't broadcast its SSID) or
736          * not (broadcasts its SSID).
737          * <p>
738          * <li>If not set, defaults to false (i.e not a hidden network).</li>
739          *
740          * @param hiddenSsid true for a hidden SSID, false otherwise.
741          * @return Builder for chaining.
742          */
743         @NonNull
setHiddenSsid(boolean hiddenSsid)744         public Builder setHiddenSsid(boolean hiddenSsid) {
745             mHiddenSsid = hiddenSsid;
746             return this;
747         }
748 
749         /**
750          * Specifies the band for the AP.
751          * <p>
752          * <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
753          *
754          * @param band One or combination of the following band type:
755          * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
756          * @return Builder for chaining.
757          */
758         @NonNull
setBand(@andType int band)759         public Builder setBand(@BandType int band) {
760             if (!isBandValid(band)) {
761                 throw new IllegalArgumentException("Invalid band type");
762             }
763             mBand = band;
764             // Since band preference is specified, no specific channel is selected.
765             mChannel = 0;
766             return this;
767         }
768 
769         /**
770          * Specifies the channel and associated band for the AP.
771          *
772          * The channel which AP resides on. Valid channels are country dependent.
773          * <p>
774          * The default for the channel is a the special value 0 to have the framework
775          * auto-select a valid channel from the band configured with
776          * {@link #setBand(int)}.
777          *
778          * The channel auto selection will offload to driver when
779          * {@link SoftApCapability#areFeaturesSupported(
780          * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
781          * return true. Driver will auto select best channel which based on environment
782          * interference to get best performance. Check {@link SoftApCapability} to get more detail.
783          *
784          * Note, since 6GHz band use the same channel numbering of 2.4GHz and 5GHZ bands,
785          * the caller needs to pass the band containing the selected channel.
786          *
787          * <p>
788          * <li>If not set, defaults to 0.</li>
789          * @param channel operating channel of the AP.
790          * @param band containing this channel.
791          * @return Builder for chaining.
792          */
793         @NonNull
setChannel(int channel, @BandType int band)794         public Builder setChannel(int channel, @BandType int band) {
795             if (!isChannelBandPairValid(channel, band)) {
796                 throw new IllegalArgumentException("Invalid band type");
797             }
798             mBand = band;
799             mChannel = channel;
800             return this;
801         }
802 
803         /**
804          * Specifies the maximum number of clients that can associate to the AP.
805          *
806          * The maximum number of clients (STAs) which can associate to the AP.
807          * The AP will reject association from any clients above this number.
808          * Specify a value of 0 to have the framework automatically use the maximum number
809          * which the device can support (based on hardware and carrier constraints).
810          * <p>
811          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
812          * {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients
813          * which the device supports (based on hardware and carrier constraints).
814          *
815          * <p>
816          * <li>If not set, defaults to 0.</li>
817          *
818          * This method requires hardware support. If the method is used to set a
819          * non-zero {@code maxNumberOfClients} value then
820          * {@link WifiManager#startTetheredHotspot} will report error code
821          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
822          *
823          * <p>
824          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
825          * {@link SoftApCapability#areFeaturesSupported(int)}
826          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
827          * or not this feature is supported.
828          *
829          * @param maxNumberOfClients maximum client number of the AP.
830          * @return Builder for chaining.
831          */
832         @NonNull
setMaxNumberOfClients(@ntRangefrom = 0) int maxNumberOfClients)833         public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
834             if (maxNumberOfClients < 0) {
835                 throw new IllegalArgumentException("maxNumberOfClients should be not negative");
836             }
837             mMaxNumberOfClients = maxNumberOfClients;
838             return this;
839         }
840 
841         /**
842          * Specifies whether auto shutdown is enabled or not.
843          * The Soft AP will shut down when there are no devices connected to it for
844          * the timeout duration.
845          *
846          * <p>
847          * <li>If not set, defaults to true</li>
848          *
849          * @param enable true to enable, false to disable.
850          * @return Builder for chaining.
851          *
852          * @see #setShutdownTimeoutMillis(long)
853          */
854         @NonNull
setAutoShutdownEnabled(boolean enable)855         public Builder setAutoShutdownEnabled(boolean enable) {
856             mAutoShutdownEnabled = enable;
857             return this;
858         }
859 
860         /**
861          * Specifies the shutdown timeout in milliseconds.
862          * The Soft AP will shut down when there are no devices connected to it for
863          * the timeout duration.
864          *
865          * Specify a value of 0 to have the framework automatically use default timeout
866          * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
867          *
868          * <p>
869          * <li>If not set, defaults to 0</li>
870          * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
871          * set to true</li>
872          *
873          * @param timeoutMillis milliseconds of the timeout delay.
874          * @return Builder for chaining.
875          *
876          * @see #setAutoShutdownEnabled(boolean)
877          */
878         @NonNull
setShutdownTimeoutMillis(@ntRangefrom = 0) long timeoutMillis)879         public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) {
880             if (timeoutMillis < 0) {
881                 throw new IllegalArgumentException("Invalid timeout value");
882             }
883             mShutdownTimeoutMillis = timeoutMillis;
884             return this;
885         }
886 
887         /**
888          * Configure the Soft AP to require manual user control of client association.
889          * If disabled (the default) then any client which isn't in the blocked list
890          * {@link #getBlockedClientList()} can associate to this Soft AP using the
891          * correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier,
892          * or user limited - using {@link #setMaxNumberOfClients(int)}).
893          *
894          * If manual user control is enabled then clients will be accepted, rejected, or require
895          * a user approval based on the configuration provided by
896          * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
897          *
898          * <p>
899          * This method requires hardware support. Hardware support can be determined using
900          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
901          * {@link SoftApCapability#areFeaturesSupported(int)}
902          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
903          *
904          * <p>
905          * If the method is called on a device without hardware support then starting the soft AP
906          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
907          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
908          *
909          * <p>
910          * <li>If not set, defaults to false (i.e The authoriztion is not required).</li>
911          *
912          * @param enabled true for enabling the control by user, false otherwise.
913          * @return Builder for chaining.
914          */
915         @NonNull
setClientControlByUserEnabled(boolean enabled)916         public Builder setClientControlByUserEnabled(boolean enabled) {
917             mClientControlByUser = enabled;
918             return this;
919         }
920 
921 
922         /**
923          * This method together with {@link setClientControlByUserEnabled(boolean)} control client
924          * connections to the AP. If client control by user is disabled using the above method then
925          * this API has no effect and clients are allowed to associate to the AP (within limit of
926          * max number of clients).
927          *
928          * If client control by user is enabled then this API configures the list of clients
929          * which are explicitly allowed. These are auto-accepted.
930          *
931          * All other clients which attempt to associate, whose MAC addresses are on neither list,
932          * are:
933          * <ul>
934          * <li>Rejected</li>
935          * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
936          * is issued (which allows the user to add them to the allowed client list if desired).<li>
937          * </ul>
938          *
939          * @param allowedClientList list of clients which are allowed to associate to the AP
940          *                          without user pre-approval.
941          * @return Builder for chaining.
942          */
943         @NonNull
setAllowedClientList(@onNull List<MacAddress> allowedClientList)944         public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
945             mAllowedClientList = new ArrayList<>(allowedClientList);
946             return this;
947         }
948 
949         /**
950          * This API configures the list of clients which are blocked and cannot associate
951          * to the Soft AP.
952          *
953          * <p>
954          * This method requires hardware support. Hardware support can be determined using
955          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
956          * {@link SoftApCapability#areFeaturesSupported(int)}
957          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
958          *
959          * <p>
960          * If the method is called on a device without hardware support then starting the soft AP
961          * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
962          * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
963          *
964          * @param blockedClientList list of clients which are not allowed to associate to the AP.
965          * @return Builder for chaining.
966          */
967         @NonNull
setBlockedClientList(@onNull List<MacAddress> blockedClientList)968         public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
969             mBlockedClientList = new ArrayList<>(blockedClientList);
970             return this;
971         }
972     }
973 }
974