blob: b1aa97bd99c9e9d6040d37905c02a5e93b2a1a09 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.wifi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.build.SdkLevel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
*
* This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
* framework how it should configure a hotspot.
*
* System apps can use this to configure a tethered hotspot using
* {@code WifiManager#startTetheredHotspot(SoftApConfiguration)} and
* {@code WifiManager#setSoftApConfiguration(SoftApConfiguration)}
* or local-only hotspot using
* {@code WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
* WifiManager.LocalOnlyHotspotCallback)}.
*
* Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to
* create a new instance.
*
*/
public final class SoftApConfiguration implements Parcelable {
private static final String TAG = "SoftApConfiguration";
@VisibleForTesting
static final int PSK_MIN_LEN = 8;
@VisibleForTesting
static final int PSK_MAX_LEN = 63;
/**
* 2GHz band.
* @hide
*/
@SystemApi
public static final int BAND_2GHZ = 1 << 0;
/**
* 5GHz band.
* @hide
*/
@SystemApi
public static final int BAND_5GHZ = 1 << 1;
/**
* 6GHz band.
* @hide
*/
@SystemApi
public static final int BAND_6GHZ = 1 << 2;
/**
* 60GHz band.
* @hide
*/
@SystemApi
public static final int BAND_60GHZ = 1 << 3;
/**
* Device is allowed to choose the optimal band (2GHz, 5GHz, 6GHz) based on device capability,
* operating country code and current radio conditions.
* @hide
*
* @deprecated This is no longer supported. The value is fixed at
* (BAND_2GHZ | BAND_5GHZ | BAND_6GHZ) even if a new band is supported in the future, for
* instance {@code BAND_60GHZ}. The bands are a bit mask - use any combination of
* {@code BAND_}, for instance {@code BAND_2GHZ | BAND_5GHZ}.
*/
@SystemApi
public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "BAND_TYPE_" }, value = {
BAND_2GHZ,
BAND_5GHZ,
BAND_6GHZ,
BAND_60GHZ,
})
public @interface BandType {}
/**
* All of the supported band types.
* @hide
*/
public static int[] BAND_TYPES = {BAND_2GHZ, BAND_5GHZ, BAND_6GHZ, BAND_60GHZ};
private static boolean isBandValid(@BandType int band) {
int bandAny = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ | BAND_60GHZ;
return ((band != 0) && ((band & ~bandAny) == 0));
}
private static final int MIN_CH_2G_BAND = 1;
private static final int MAX_CH_2G_BAND = 14;
private static final int MIN_CH_5G_BAND = 34;
private static final int MAX_CH_5G_BAND = 196;
private static final int MIN_CH_6G_BAND = 1;
private static final int MAX_CH_6G_BAND = 253;
private static final int MIN_CH_60G_BAND = 1;
private static final int MAX_CH_60G_BAND = 6;
private static boolean isChannelBandPairValid(int channel, @BandType int band) {
switch (band) {
case BAND_2GHZ:
if (channel < MIN_CH_2G_BAND || channel > MAX_CH_2G_BAND) {
return false;
}
break;
case BAND_5GHZ:
if (channel < MIN_CH_5G_BAND || channel > MAX_CH_5G_BAND) {
return false;
}
break;
case BAND_6GHZ:
if (channel < MIN_CH_6G_BAND || channel > MAX_CH_6G_BAND) {
return false;
}
break;
case BAND_60GHZ:
if (channel < MIN_CH_60G_BAND || channel > MAX_CH_60G_BAND) {
return false;
}
break;
default:
return false;
}
return true;
}
/**
* SSID for the AP, or null for a framework-determined SSID.
*/
private final @Nullable String mSsid;
/**
* BSSID for the AP, or null to use a framework-determined BSSID.
*/
private final @Nullable MacAddress mBssid;
/**
* Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on
* the security type.
*/
private final @Nullable String mPassphrase;
/**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
*/
private final boolean mHiddenSsid;
/**
* The operating channels of the dual APs.
*
* The SparseIntArray that consists the band and the channel of matching the band.
*/
@NonNull
private final SparseIntArray mChannels;
/**
* The maximim allowed number of clients that can associate to the AP.
*/
private final int mMaxNumberOfClients;
/**
* The operating security type of the AP.
* One of the following security types:
* {@link #SECURITY_TYPE_OPEN},
* {@link #SECURITY_TYPE_WPA2_PSK},
* {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
* {@link #SECURITY_TYPE_WPA3_SAE}
*/
private final @SecurityType int mSecurityType;
/**
* The flag to indicate client need to authorize by user
* when client is connecting to AP.
*/
private final boolean mClientControlByUser;
/**
* The list of blocked client that can't associate to the AP.
*/
private final List<MacAddress> mBlockedClientList;
/**
* The list of allowed client that can associate to the AP.
*/
private final List<MacAddress> mAllowedClientList;
/**
* Whether auto shutdown of soft AP is enabled or not.
*/
private final boolean mAutoShutdownEnabled;
/**
* Delay in milliseconds before shutting down soft AP when
* there are no connected devices.
*/
private final long mShutdownTimeoutMillis;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"RANDOMIZATION_"}, value = {
RANDOMIZATION_NONE,
RANDOMIZATION_PERSISTENT})
public @interface MacRandomizationSetting {}
/**
* Use factory MAC as BSSID for the AP
* @hide
*/
@SystemApi
public static final int RANDOMIZATION_NONE = 0;
/**
* Generate a randomized MAC as BSSID for the AP
* @hide
*/
@SystemApi
public static final int RANDOMIZATION_PERSISTENT = 1;
/**
* Level of MAC randomization for the AP BSSID.
*/
@MacRandomizationSetting
private int mMacRandomizationSetting;
/**
* Whether opportunistic shutdown of an instance in bridged AP is enabled or not.
*/
private boolean mBridgedModeOpportunisticShutdownEnabled;
/**
* Whether 802.11ax AP is enabled or not.
*/
private boolean mIeee80211axEnabled;
/**
* Whether the current configuration is configured by user or not.
*/
private boolean mIsUserConfiguration;
/**
* THe definition of security type OPEN.
*/
public static final int SECURITY_TYPE_OPEN = 0;
/**
* The definition of security type WPA2-PSK.
*/
public static final int SECURITY_TYPE_WPA2_PSK = 1;
/**
* The definition of security type WPA3-SAE Transition mode.
*/
public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2;
/**
* The definition of security type WPA3-SAE.
*/
public static final int SECURITY_TYPE_WPA3_SAE = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "SECURITY_TYPE_" }, value = {
SECURITY_TYPE_OPEN,
SECURITY_TYPE_WPA2_PSK,
SECURITY_TYPE_WPA3_SAE_TRANSITION,
SECURITY_TYPE_WPA3_SAE,
})
public @interface SecurityType {}
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String 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 isUserConfiguration) {
mSsid = ssid;
mBssid = bssid;
mPassphrase = passphrase;
mHiddenSsid = hiddenSsid;
if (channels.size() != 0) {
mChannels = channels.clone();
} else {
mChannels = new SparseIntArray(1);
mChannels.put(BAND_2GHZ, 0);
}
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
mAutoShutdownEnabled = shutdownTimeoutEnabled;
mShutdownTimeoutMillis = shutdownTimeoutMillis;
mClientControlByUser = clientControlByUser;
mBlockedClientList = new ArrayList<>(blockedList);
mAllowedClientList = new ArrayList<>(allowedList);
mMacRandomizationSetting = macRandomizationSetting;
mBridgedModeOpportunisticShutdownEnabled = bridgedModeOpportunisticShutdownEnabled;
mIeee80211axEnabled = ieee80211axEnabled;
mIsUserConfiguration = isUserConfiguration;
}
@Override
public boolean equals(Object otherObj) {
if (this == otherObj) {
return true;
}
if (!(otherObj instanceof SoftApConfiguration)) {
return false;
}
SoftApConfiguration other = (SoftApConfiguration) otherObj;
return Objects.equals(mSsid, other.mSsid)
&& Objects.equals(mBssid, other.mBssid)
&& Objects.equals(mPassphrase, other.mPassphrase)
&& mHiddenSsid == other.mHiddenSsid
&& mChannels.toString().equals(other.mChannels.toString())
&& mSecurityType == other.mSecurityType
&& mMaxNumberOfClients == other.mMaxNumberOfClients
&& mAutoShutdownEnabled == other.mAutoShutdownEnabled
&& mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
&& mClientControlByUser == other.mClientControlByUser
&& Objects.equals(mBlockedClientList, other.mBlockedClientList)
&& Objects.equals(mAllowedClientList, other.mAllowedClientList)
&& mMacRandomizationSetting == other.mMacRandomizationSetting
&& mBridgedModeOpportunisticShutdownEnabled
== other.mBridgedModeOpportunisticShutdownEnabled
&& mIeee80211axEnabled == other.mIeee80211axEnabled
&& mIsUserConfiguration == other.mIsUserConfiguration;
}
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
mChannels.toString(), mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
mAllowedClientList, mMacRandomizationSetting,
mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled,
mIsUserConfiguration);
}
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
sbuf.append("ssid = ").append(mSsid);
if (mBssid != null) sbuf.append(" \n bssid = ").append(mBssid.toString());
sbuf.append(" \n Passphrase = ").append(
TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>");
sbuf.append(" \n HiddenSsid = ").append(mHiddenSsid);
sbuf.append(" \n Channels = ").append(mChannels);
sbuf.append(" \n SecurityType = ").append(getSecurityType());
sbuf.append(" \n MaxClient = ").append(mMaxNumberOfClients);
sbuf.append(" \n AutoShutdownEnabled = ").append(mAutoShutdownEnabled);
sbuf.append(" \n ShutdownTimeoutMillis = ").append(mShutdownTimeoutMillis);
sbuf.append(" \n ClientControlByUser = ").append(mClientControlByUser);
sbuf.append(" \n BlockedClientList = ").append(mBlockedClientList);
sbuf.append(" \n AllowedClientList= ").append(mAllowedClientList);
sbuf.append(" \n MacRandomizationSetting = ").append(mMacRandomizationSetting);
sbuf.append(" \n BridgedModeInstanceOpportunisticEnabled = ")
.append(mBridgedModeOpportunisticShutdownEnabled);
sbuf.append(" \n Ieee80211axEnabled = ").append(mIeee80211axEnabled);
sbuf.append(" \n isUserConfiguration = ").append(mIsUserConfiguration);
return sbuf.toString();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mSsid);
dest.writeParcelable(mBssid, flags);
dest.writeString(mPassphrase);
dest.writeBoolean(mHiddenSsid);
writeSparseIntArray(dest, mChannels);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
dest.writeBoolean(mAutoShutdownEnabled);
dest.writeLong(mShutdownTimeoutMillis);
dest.writeBoolean(mClientControlByUser);
dest.writeTypedList(mBlockedClientList);
dest.writeTypedList(mAllowedClientList);
dest.writeInt(mMacRandomizationSetting);
dest.writeBoolean(mBridgedModeOpportunisticShutdownEnabled);
dest.writeBoolean(mIeee80211axEnabled);
dest.writeBoolean(mIsUserConfiguration);
}
/* Reference from frameworks/base/core/java/android/os/Parcel.java */
private static void writeSparseIntArray(@NonNull Parcel dest,
@Nullable SparseIntArray val) {
if (val == null) {
dest.writeInt(-1);
return;
}
int n = val.size();
dest.writeInt(n);
int i = 0;
while (i < n) {
dest.writeInt(val.keyAt(i));
dest.writeInt(val.valueAt(i));
i++;
}
}
/* Reference from frameworks/base/core/java/android/os/Parcel.java */
@NonNull
private static SparseIntArray readSparseIntArray(@NonNull Parcel in) {
int n = in.readInt();
if (n < 0) {
return new SparseIntArray();
}
SparseIntArray sa = new SparseIntArray(n);
while (n > 0) {
int key = in.readInt();
int value = in.readInt();
sa.append(key, value);
n--;
}
return sa;
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() {
@Override
public SoftApConfiguration createFromParcel(Parcel in) {
return new SoftApConfiguration(
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), readSparseIntArray(in), in.readInt(),
in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(),
in.createTypedArrayList(MacAddress.CREATOR),
in.createTypedArrayList(MacAddress.CREATOR), in.readInt(), in.readBoolean(),
in.readBoolean(), in.readBoolean());
}
@Override
public SoftApConfiguration[] newArray(int size) {
return new SoftApConfiguration[size];
}
};
/**
* Return String set to be the SSID for the AP.
* See also {@link Builder#setSsid(String)}.
*/
@Nullable
public String getSsid() {
return mSsid;
}
/**
* Returns MAC address set to be BSSID for the AP.
* See also {@link Builder#setBssid(MacAddress)}.
*/
@Nullable
public MacAddress getBssid() {
return mBssid;
}
/**
* Returns String set to be passphrase for current AP.
* See also {@link Builder#setPassphrase(String, int)}.
*/
@Nullable
public String getPassphrase() {
return mPassphrase;
}
/**
* Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
* not (false: broadcasts its SSID) for the AP.
* See also {@link Builder#setHiddenSsid(boolean)}.
*/
public boolean isHiddenSsid() {
return mHiddenSsid;
}
/**
* Returns band type set to be the band for the AP.
*
* One or combination of {@code BAND_}, for instance
* {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, or {@code BAND_2GHZ | BAND_5GHZ}.
*
* Note: Returns the lowest band when more than one band is set.
* Use {@link #getChannels()} to get dual bands setting.
*
* See also {@link Builder#setBand(int)}.
*
* @deprecated This API is deprecated. Use {@link #getChannels()} instead.
* @hide
*/
@Deprecated
@SystemApi
public @BandType int getBand() {
return mChannels.keyAt(0);
}
/**
* Returns a sorted array in ascending order that consists of the configured band types
* for the APs.
*
* The band type is one or combination of {@code BAND_}, for instance
* {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, or {@code BAND_2GHZ | BAND_5GHZ}.
*
* Note: return array may only include one band when current setting is single AP mode.
* See also {@link Builder#setBands(int[])}.
*
* @hide
*/
public @NonNull int[] getBands() {
int[] bands = new int[mChannels.size()];
for (int i = 0; i < bands.length; i++) {
bands[i] = mChannels.keyAt(i);
}
return bands;
}
/**
* Returns Integer set to be the channel for the AP.
*
* Note: Returns the channel which associated to the lowest band if more than one channel
* is set. Use {@link Builder#getChannels()} to get dual channel setting.
* See also {@link Builder#setChannel(int, int)}.
*
* @deprecated This API is deprecated. Use {@link #getChannels()} instead.
* @hide
*/
@Deprecated
@SystemApi
public int getChannel() {
return mChannels.valueAt(0);
}
/**
* Returns SparseIntArray (key: {@code BandType} , value: channel) that consists of
* the configured bands and channels for the AP(s).
*
* The returned channel value is Wi-Fi channel numbering.
* Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016
* specifications, section 17.3.8.4.2, 17.3.8.4.3 and Table 15-6.
*
* Note: return array may only include one channel when current setting is single AP mode.
* See also {@link Builder#setChannels(SparseIntArray)}.
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
public @NonNull SparseIntArray getChannels() {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mChannels.clone();
}
/**
* Get security type params which depends on which security passphrase to set.
*
* @return One of:
* {@link #SECURITY_TYPE_OPEN},
* {@link #SECURITY_TYPE_WPA2_PSK},
* {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
* {@link #SECURITY_TYPE_WPA3_SAE}
*/
public @SecurityType int getSecurityType() {
return mSecurityType;
}
/**
* Returns the maximum number of clients that can associate to the AP.
* See also {@link Builder#setMaxNumberOfClients(int)}.
*
* @hide
*/
@SystemApi
public int getMaxNumberOfClients() {
return mMaxNumberOfClients;
}
/**
* Returns whether auto shutdown is enabled or not.
* The Soft AP will shutdown when there are no devices associated to it for
* the timeout duration. See also {@link Builder#setAutoShutdownEnabled(boolean)}.
*
* @hide
*/
@SystemApi
public boolean isAutoShutdownEnabled() {
return mAutoShutdownEnabled;
}
/**
* Returns the shutdown timeout in milliseconds.
* The Soft AP will shutdown when there are no devices associated to it for
* the timeout duration. See also {@link Builder#setShutdownTimeoutMillis(long)}.
*
* @hide
*/
@SystemApi
public long getShutdownTimeoutMillis() {
return mShutdownTimeoutMillis;
}
/**
* Returns a flag indicating whether clients need to be pre-approved by the user.
* (true: authorization required) or not (false: not required).
* See also {@link Builder#setClientControlByUserEnabled(Boolean)}.
*
* @hide
*/
@SystemApi
public boolean isClientControlByUserEnabled() {
return mClientControlByUser;
}
/**
* Returns List of clients which aren't allowed to associate to the AP.
*
* Clients are configured using {@link Builder#setBlockedClientList(List)}
*
* @hide
*/
@NonNull
@SystemApi
public List<MacAddress> getBlockedClientList() {
return mBlockedClientList;
}
/**
* List of clients which are allowed to associate to the AP.
* Clients are configured using {@link Builder#setAllowedClientList(List)}
*
* @hide
*/
@NonNull
@SystemApi
public List<MacAddress> getAllowedClientList() {
return mAllowedClientList;
}
/**
* Returns the level of MAC randomization for the AP BSSID.
* See also {@link Builder#setMacRandomizationSetting(int)}.
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
@MacRandomizationSetting
public int getMacRandomizationSetting() {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return getMacRandomizationSettingInternal();
}
/**
* @hide
*/
@MacRandomizationSetting
public int getMacRandomizationSettingInternal() {
return mMacRandomizationSetting;
}
/**
* Returns whether opportunistic shutdown of an instance in bridged AP is enabled or not.
*
* See also {@link Builder#setBridgedModeOpportunisticShutdownEnabled(boolean}}
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
public boolean isBridgedModeOpportunisticShutdownEnabled() {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return isBridgedModeOpportunisticShutdownEnabledInternal();
}
/**
* @see #isBridgedModeOpportunisticShutdownEnabled()
* @hide
*/
public boolean isBridgedModeOpportunisticShutdownEnabledInternal() {
return mBridgedModeOpportunisticShutdownEnabled;
}
/**
* @see #isIeee80211axEnabled()
* @hide
*/
public boolean isIeee80211axEnabledInternal() {
return mIeee80211axEnabled;
}
/**
* Returns whether or not 802.11ax is enabled on the SoftAP.
* This is an indication that if the device support 802.11ax AP then to enable or disable
* that feature. If the device does not support 802.11ax AP then this flag is ignored.
* See also {@link Builder#setIeee80211axEnabled(boolean}}
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
public boolean isIeee80211axEnabled() {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return isIeee80211axEnabledInternal();
}
/**
* Returns whether or not the {@link SoftApConfiguration} was configured by the user
* (as opposed to the default system configuration).
* <p>
* The {@link SoftApConfiguration} is considered user edited once the
* {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)} is called
* - whether or not that configuration is the same as the default system configuration!
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
public boolean isUserConfiguration() {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return isUserConfigurationInternal();
}
/**
* @hide
*/
public boolean isUserConfigurationInternal() {
return mIsUserConfiguration;
}
/**
* Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}.
* Note that SoftApConfiguration may contain configuration which is cannot be represented
* by the legacy WifiConfiguration, in such cases a null will be returned.
*
* <li> SoftAp band in {@link WifiConfiguration.apBand} only supports
* 2GHz, 5GHz, 2GHz+5GHz bands, so conversion is limited to these bands. </li>
*
* <li> SoftAp security type in {@link WifiConfiguration.KeyMgmt} only supports
* NONE, WPA2_PSK, so conversion is limited to these security type.</li>
* @hide
*/
@Nullable
@SystemApi
public WifiConfiguration toWifiConfiguration() {
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.SSID = mSsid;
wifiConfig.preSharedKey = mPassphrase;
wifiConfig.hiddenSSID = mHiddenSsid;
wifiConfig.apChannel = getChannel();
switch (mSecurityType) {
case SECURITY_TYPE_OPEN:
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case SECURITY_TYPE_WPA2_PSK:
case SECURITY_TYPE_WPA3_SAE_TRANSITION:
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
break;
default:
Log.e(TAG, "Convert fail, unsupported security type :" + mSecurityType);
return null;
}
switch (getBand()) {
case BAND_2GHZ:
wifiConfig.apBand = WifiConfiguration.AP_BAND_2GHZ;
break;
case BAND_5GHZ:
wifiConfig.apBand = WifiConfiguration.AP_BAND_5GHZ;
break;
case BAND_2GHZ | BAND_5GHZ:
wifiConfig.apBand = WifiConfiguration.AP_BAND_ANY;
break;
case BAND_ANY:
wifiConfig.apBand = WifiConfiguration.AP_BAND_ANY;
break;
default:
Log.e(TAG, "Convert fail, unsupported band setting :" + getBand());
return null;
}
return wifiConfig;
}
/**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
*
* All fields are optional. By default, SSID and BSSID are automatically chosen by the
* framework, and an open network is created.
*
* @hide
*/
@SystemApi
public static final class Builder {
private String mSsid;
private MacAddress mBssid;
private String mPassphrase;
private boolean mHiddenSsid;
private SparseIntArray mChannels;
private int mMaxNumberOfClients;
private int mSecurityType;
private boolean mAutoShutdownEnabled;
private long mShutdownTimeoutMillis;
private boolean mClientControlByUser;
private List<MacAddress> mBlockedClientList;
private List<MacAddress> mAllowedClientList;
private int mMacRandomizationSetting;
private boolean mBridgedModeOpportunisticShutdownEnabled;
private boolean mIeee80211axEnabled;
private boolean mIsUserConfiguration;
/**
* Constructs a Builder with default values (see {@link Builder}).
*/
public Builder() {
mSsid = null;
mBssid = null;
mPassphrase = null;
mHiddenSsid = false;
mChannels = new SparseIntArray(1);
mChannels.put(BAND_2GHZ, 0);
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
mAutoShutdownEnabled = true; // enabled by default.
mShutdownTimeoutMillis = 0;
mClientControlByUser = false;
mBlockedClientList = new ArrayList<>();
mAllowedClientList = new ArrayList<>();
mMacRandomizationSetting = RANDOMIZATION_PERSISTENT;
mBridgedModeOpportunisticShutdownEnabled = true;
mIeee80211axEnabled = true;
mIsUserConfiguration = true;
}
/**
* Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
*/
public Builder(@NonNull SoftApConfiguration other) {
Objects.requireNonNull(other);
mSsid = other.mSsid;
mBssid = other.mBssid;
mPassphrase = other.mPassphrase;
mHiddenSsid = other.mHiddenSsid;
mChannels = other.mChannels.clone();
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
mAutoShutdownEnabled = other.mAutoShutdownEnabled;
mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
mClientControlByUser = other.mClientControlByUser;
mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
mMacRandomizationSetting = other.mMacRandomizationSetting;
mBridgedModeOpportunisticShutdownEnabled =
other.mBridgedModeOpportunisticShutdownEnabled;
mIeee80211axEnabled = other.mIeee80211axEnabled;
mIsUserConfiguration = other.mIsUserConfiguration;
}
/**
* Builds the {@link SoftApConfiguration}.
*
* @return A new {@link SoftApConfiguration}, as configured by previous method calls.
*/
@NonNull
public SoftApConfiguration build() {
for (MacAddress client : mAllowedClientList) {
if (mBlockedClientList.contains(client)) {
throw new IllegalArgumentException("A MacAddress exist in both client list");
}
}
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
mHiddenSsid, mChannels, mSecurityType, mMaxNumberOfClients,
mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
mBlockedClientList, mAllowedClientList, mMacRandomizationSetting,
mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled,
mIsUserConfiguration);
}
/**
* Specifies an SSID for the AP.
* <p>
* Null SSID only support when configure a local-only hotspot.
* <p>
* <li>If not set, defaults to null.</li>
*
* @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
* chosen by the framework.
* @return Builder for chaining.
* @throws IllegalArgumentException when the SSID is empty or not valid Unicode.
*/
@NonNull
public Builder setSsid(@Nullable String ssid) {
if (ssid != null) {
Preconditions.checkStringNotEmpty(ssid);
Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
}
mSsid = ssid;
return this;
}
/**
* Specifies a BSSID for the AP.
* <p>
* <li>If not set, defaults to null.</li>
*
* If multiple bands are requested via {@link #setBands(int[])} or
* {@link #setChannels(SparseIntArray)}, HAL will derive 2 MAC addresses since framework
* only sends down 1 MAC address.
*
* An example (but different implementation may perform a different mapping):
* <li>MAC address 1: copy value of MAC address,
* and set byte 1 = (0xFF - BSSID[1])</li>
* <li>MAC address 2: copy value of MAC address,
* and set byte 2 = (0xFF - BSSID[2])</li>
*
* Example BSSID argument: e2:38:60:c4:0e:b7
* Derived MAC address 1: e2:c7:60:c4:0e:b7
* Derived MAC address 2: e2:38:9f:c4:0e:b7
*
* <p>
* Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine
* whether or not this feature is supported.
*
* @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
* responsible for avoiding collisions.
* @return Builder for chaining.
* @throws IllegalArgumentException when the given BSSID is the all-zero
* , multicast or broadcast MAC address.
*/
@NonNull
public Builder setBssid(@Nullable MacAddress bssid) {
if (bssid != null) {
Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) {
throw new IllegalArgumentException("bssid doesn't support "
+ "multicast or broadcast mac address");
}
}
mBssid = bssid;
return this;
}
/**
* Specifies that this AP should use specific security type with the given ASCII passphrase.
*
* @param securityType One of the following security types:
* {@link #SECURITY_TYPE_OPEN},
* {@link #SECURITY_TYPE_WPA2_PSK},
* {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
* {@link #SECURITY_TYPE_WPA3_SAE}.
* @param passphrase The passphrase to use for sepcific {@code securityType} configuration
* or null with {@link #SECURITY_TYPE_OPEN}.
*
* @return Builder for chaining.
* @throws IllegalArgumentException when the passphrase length is invalid and
* {@code securityType} is not {@link #SECURITY_TYPE_OPEN}
* or non-null passphrase and {@code securityType} is
* {@link #SECURITY_TYPE_OPEN}.
*/
@NonNull
public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
if (securityType == SECURITY_TYPE_OPEN) {
if (passphrase != null) {
throw new IllegalArgumentException(
"passphrase should be null when security type is open");
}
} else {
Preconditions.checkStringNotEmpty(passphrase);
if (securityType == SECURITY_TYPE_WPA2_PSK
|| securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
throw new IllegalArgumentException(
"Password size must be at least " + PSK_MIN_LEN
+ " and no more than " + PSK_MAX_LEN
+ " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
}
}
}
mSecurityType = securityType;
mPassphrase = passphrase;
return this;
}
/**
* Specifies whether the AP is hidden (doesn't broadcast its SSID) or
* not (broadcasts its SSID).
* <p>
* <li>If not set, defaults to false (i.e not a hidden network).</li>
*
* @param hiddenSsid true for a hidden SSID, false otherwise.
* @return Builder for chaining.
*/
@NonNull
public Builder setHiddenSsid(boolean hiddenSsid) {
mHiddenSsid = hiddenSsid;
return this;
}
/**
* Specifies the band for the AP.
* <p>
* <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
*
* @param band One or combination of the following band type:
* {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
* @return Builder for chaining.
* @throws IllegalArgumentException when an invalid band type is provided.
*/
@NonNull
public Builder setBand(@BandType int band) {
if (!isBandValid(band)) {
throw new IllegalArgumentException("Invalid band type: " + band);
}
mChannels = new SparseIntArray(1);
mChannels.put(band, 0);
return this;
}
/**
* Specifies the bands for the APs.
* If more than 1 band is set, this will bring up concurrent APs.
* on the requested bands (if possible).
* <p>
*
* Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
* whether or not concurrent APs are supported.
*
* Requires the driver to support {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
* when multiple bands are configured. Otherwise,
* {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* Note: Only supports 2.4GHz + 5GHz bands. If any other band is set, will report error
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* @param bands Array of the {@link #BandType}.
* @return Builder for chaining.
* @throws IllegalArgumentException when more than 2 bands are set or an invalid band type
* is provided.
*/
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
public Builder setBands(@NonNull int[] bands) {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
if (bands.length == 0 || bands.length > 2) {
throw new IllegalArgumentException("Unsupported number of bands("
+ bands.length + ") configured");
}
SparseIntArray channels = new SparseIntArray(bands.length);
for (int val : bands) {
if (!isBandValid(val)) {
throw new IllegalArgumentException("Invalid band type: " + val);
}
channels.put(val, 0);
}
mChannels = channels;
return this;
}
/**
* Specifies the channel and associated band for the AP.
*
* The channel which AP resides on. Valid channels are country dependent.
* The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
* valid channels.
*
* <p>
* If not set, the default for the channel is the special value 0 which has the
* framework auto-select a valid channel from the band configured with
* {@link #setBand(int)}.
*
* The channel auto selection will be offloaded to driver when
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
* return true. The driver will auto select the best channel (e.g. best performance)
* based on environment interference. Check {@link SoftApCapability} for more detail.
*
* The API contains (band, channel) input since the 6GHz band uses the same channel
* numbering scheme as is used in the 2.4GHz and 5GHz band. Therefore, both are needed to
* uniquely identify individual channels.
*
* <p>
* @param channel operating channel of the AP.
* @param band containing this channel.
* @return Builder for chaining.
* @throws IllegalArgumentException when the invalid channel or band type is configured.
*/
@NonNull
public Builder setChannel(int channel, @BandType int band) {
if (!isChannelBandPairValid(channel, band)) {
throw new IllegalArgumentException("Invalid channel(" + channel
+ ") & band (" + band + ") configured");
}
mChannels = new SparseIntArray(1);
mChannels.put(band, channel);
return this;
}
/**
* Specifies the channels and associated bands for the APs.
*
* When more than 1 channel is set, this will bring up concurrent APs on the requested
* channels and bands (if possible).
*
* Valid channels are country dependent.
* The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
* valid channels in each band.
*
* Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
* whether or not concurrent APs are supported.
*
* <p>
* If not set, the default for the channel is the special value 0 which has the framework
* auto-select a valid channel from the band configured with {@link #setBands(int[])}.
*
* The channel auto selection will be offloaded to driver when
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
* returns true. The driver will auto select the best channel (e.g. best performance)
* based on environment interference. Check {@link SoftApCapability} for more detail.
*
* Requires the driver to support {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD}
* when multiple bands are configured without specified channel value (i.e. channel is
* the special value 0). Otherwise,
* {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* Note: Only supports 2.4GHz + 5GHz bands. If any other band is set, will report error
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* The API contains (band, channel) input since the 6GHz band uses the same channel
* numbering scheme as is used in the 2.4GHz and 5GHz band. Therefore, both are needed to
* uniquely identify individual channels.
*
* Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016
* specifications, section 17.3.8.4.2, 17.3.8.4.3 and Table 15-6.
*
* <p>
* @param channels SparseIntArray (key: {@code #BandType} , value: channel) consists of
* {@code BAND_} and corresponding channel.
* @return Builder for chaining.
* @throws IllegalArgumentException when more than 2 channels are set or the invalid
* channel or band type is configured.
*/
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
public Builder setChannels(@NonNull SparseIntArray channels) {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
if (channels.size() == 0 || channels.size() > 2) {
throw new IllegalArgumentException("Unsupported number of channels("
+ channels.size() + ") configured");
}
for (int i = 0; i < channels.size(); i++) {
int channel = channels.valueAt(i);
int band = channels.keyAt(i);
if (channel == 0) {
if (!isBandValid(band)) {
throw new IllegalArgumentException("Invalid band type: " + band);
}
} else {
if (!isChannelBandPairValid(channel, band)) {
throw new IllegalArgumentException("Invalid channel(" + channel
+ ") & band (" + band + ") configured");
}
}
}
mChannels = channels.clone();
return this;
}
/**
* Specifies the maximum number of clients that can associate to the AP.
*
* The maximum number of clients (STAs) which can associate to the AP.
* The AP will reject association from any clients above this number.
* Specify a value of 0 to have the framework automatically use the maximum number
* which the device can support (based on hardware and carrier constraints).
* <p>
* Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients
* which the device supports (based on hardware and carrier constraints).
*
* <p>
* <li>If not set, defaults to 0.</li>
*
* This method requires HAL support. If the method is used to set a
* non-zero {@code maxNumberOfClients} value then
* {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* <p>
* Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
* or not this feature is supported.
*
* @param maxNumberOfClients maximum client number of the AP.
* @return Builder for chaining.
*/
@NonNull
public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
if (maxNumberOfClients < 0) {
throw new IllegalArgumentException("maxNumberOfClients should be not negative");
}
mMaxNumberOfClients = maxNumberOfClients;
return this;
}
/**
* Specifies whether auto shutdown is enabled or not.
* The Soft AP will shut down when there are no devices connected to it for
* the timeout duration.
*
* <p>
* <li>If not set, defaults to true</li>
*
* @param enable true to enable, false to disable.
* @return Builder for chaining.
*
* @see #setShutdownTimeoutMillis(long)
*/
@NonNull
public Builder setAutoShutdownEnabled(boolean enable) {
mAutoShutdownEnabled = enable;
return this;
}
/**
* Specifies the shutdown timeout in milliseconds.
* The Soft AP will shut down when there are no devices connected to it for
* the timeout duration.
*
* Specify a value of 0 to have the framework automatically use default timeout
* setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
*
* <p>
* <li>If not set, defaults to 0</li>
* <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
* set to true</li>
*
* @param timeoutMillis milliseconds of the timeout delay.
* @return Builder for chaining.
*
* @see #setAutoShutdownEnabled(boolean)
*/
@NonNull
public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("Invalid timeout value");
}
mShutdownTimeoutMillis = timeoutMillis;
return this;
}
/**
* Configure the Soft AP to require manual user control of client association.
* If disabled (the default) then any client which isn't in the blocked list
* {@link #getBlockedClientList()} can associate to this Soft AP using the
* correct credentials until the Soft AP capacity is reached (capacity is hardware, carrier,
* or user limited - using {@link #setMaxNumberOfClients(int)}).
*
* If manual user control is enabled then clients will be accepted, rejected, or require
* a user approval based on the configuration provided by
* {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
*
* <p>
* This method requires HAL support. HAL support can be determined using
* {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
*
* <p>
* If the method is called on a device without HAL support then starting the soft AP
* using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* <p>
* <li>If not set, defaults to false (i.e The authoriztion is not required).</li>
*
* @param enabled true for enabling the control by user, false otherwise.
* @return Builder for chaining.
*/
@NonNull
public Builder setClientControlByUserEnabled(boolean enabled) {
mClientControlByUser = enabled;
return this;
}
/**
* This method together with {@link setClientControlByUserEnabled(boolean)} control client
* connections to the AP. If client control by user is disabled using the above method then
* this API has no effect and clients are allowed to associate to the AP (within limit of
* max number of clients).
*
* If client control by user is enabled then this API configures the list of clients
* which are explicitly allowed. These are auto-accepted.
*
* All other clients which attempt to associate, whose MAC addresses are on neither list,
* are:
* <ul>
* <li>Rejected</li>
* <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
* is issued (which allows the user to add them to the allowed client list if desired).<li>
* </ul>
*
* @param allowedClientList list of clients which are allowed to associate to the AP
* without user pre-approval.
* @return Builder for chaining.
*/
@NonNull
public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
mAllowedClientList = new ArrayList<>(allowedClientList);
return this;
}
/**
* This API configures the list of clients which are blocked and cannot associate
* to the Soft AP.
*
* <p>
* This method requires HAL support. HAL support can be determined using
* {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
*
* <p>
* If the method is called on a device without HAL support then starting the soft AP
* using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* @param blockedClientList list of clients which are not allowed to associate to the AP.
* @return Builder for chaining.
*/
@NonNull
public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
mBlockedClientList = new ArrayList<>(blockedClientList);
return this;
}
/**
* Specifies the level of MAC randomization for the AP BSSID.
* The Soft AP BSSID will be randomized only if the BSSID isn't set
* {@link #setBssid(MacAddress)} and this method is either uncalled
* or called with {@link #RANDOMIZATION_PERSISTENT}.
*
* <p>
* <li>If not set, defaults to {@link #RANDOMIZATION_PERSISTENT}</li>
*
* <p>
* Requires HAL support when set to {@link #RANDOMIZATION_PERSISTENT}.
* Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine
* whether or not this feature is supported.
*
* @param macRandomizationSetting One of the following setting:
* {@link #RANDOMIZATION_NONE} or {@link #RANDOMIZATION_PERSISTENT}.
* @return Builder for chaining.
*
* @see #setBssid(MacAddress)
*/
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
public Builder setMacRandomizationSetting(
@MacRandomizationSetting int macRandomizationSetting) {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mMacRandomizationSetting = macRandomizationSetting;
return this;
}
/**
* Specifies whether or not opportunistic shut down of an AP instance in bridged mode
* is enabled.
*
* <p>
* If enabled, the framework will shutdown one of the AP instances if it is idle for
* the timeout duration - meaning there are no devices connected to it.
* If both AP instances are idle for the timeout duration then the framework will
* shut down the AP instance operating on the higher frequency. For instance,
* if the AP instances operate at 2.4GHz and 5GHz and are both idle for the
* timeout duration then the 5GHz AP instance will be shut down.
* <p>
*
* Note: the opportunistic timeout only applies to one AP instance of the bridge AP.
* If one of the AP instances has already been disabled for any reason, including due to
* an opportunistic timeout or hardware issues or coexistence issues,
* then the opportunistic timeout is no longer active.
*
* <p>
* The shutdown timer specified by {@link #setShutdownTimeoutMillis(long)} controls the
* overall shutdown of the bridged AP and is still in use independently of the opportunistic
* timer controlled by this AP.
*
* <p>
* <li>If not set, defaults to true</li>
*
* @param enable true to enable, false to disable.
* @return Builder for chaining.
*
*/
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
public Builder setBridgedModeOpportunisticShutdownEnabled(boolean enable) {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mBridgedModeOpportunisticShutdownEnabled = enable;
return this;
}
/**
* Specifies whether or not to enable 802.11ax on the Soft AP.
*
* <p>
* Note: Only relevant when the device supports 802.11ax on the Soft AP.
* If enabled on devices that do not support 802.11ax then ignored.
* Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
* {@link SoftApCapability#areFeaturesSupported(long)}
* with {@link SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX} to determine
* whether or not 802.11ax is supported on the Soft AP.
* <p>
* <li>If not set, defaults to true - which will be ignored on devices
* which do not support 802.11ax</li>
*
* @param enable true to enable, false to disable.
* @return Builder for chaining.
*
*/
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
public Builder setIeee80211axEnabled(boolean enable) {
if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mIeee80211axEnabled = enable;
return this;
}
/**
* Specifies whether or not the configuration is configured by user.
*
* @param isUserConfigured true to user configuration, false otherwise.
* @return Builder for chaining.
*
* @hide
*/
@NonNull
public Builder setUserConfiguration(boolean isUserConfigured) {
mIsUserConfiguration = isUserConfigured;
return this;
}
}
}