/* * 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.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.net.MacAddress; import android.net.wifi.util.HexEncoding; 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 androidx.annotation.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * 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 or local-only hotspot. * *

Instances of this class are immutable. */ 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; /** * A default value used to configure shut down timeout setting to default value. * See {@link Builder#setShutdownTimeoutMillis(long)} or * {@link Builder#setBridgedModeOpportunisticShutdownTimeoutMillis(long)} for details. * * @hide */ @SystemApi public static final long DEFAULT_TIMEOUT = -1; /** @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; /** * Requires to configure MAC randomization setting to None when configuring BSSID. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) private static final long FORCE_MUTUAL_EXCLUSIVE_BSSID_MAC_RAMDONIZATION_SETTING = 215656264L; /** * Removes zero support on * {@link android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)}. * * @hide */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) public static final long REMOVE_ZERO_FOR_TIMEOUT_SETTING = 213289672L; 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 WifiSsid mWifiSsid; /** * BSSID for the AP, or null to use a framework-determined BSSID. */ private final @Nullable MacAddress mBssid; /** * Vendor elements for the AP, structured as dd+len+elements */ private final @NonNull List mVendorElements; /** * 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 set of allowed channels in 2.4GHz band to select from using ACS (Automatic Channel * Selection) algorithm. * * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}. * Otherwise, this set will be ignored. * * If the set is empty, then all channels in 2.4GHz band are allowed. */ private final @NonNull Set mAllowedAcsChannels2g; /** * The set of allowed channels in 5GHz band to select from using ACS (Automatic Channel * Selection) algorithm. * * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}. * Otherwise, this set will be ignored. * * If the set is empty, then all channels in 5GHz are allowed. */ private final @NonNull Set mAllowedAcsChannels5g; /** * The set of allowed channels in 6GHz band to select from using ACS (Automatic Channel * Selection) algorithm. * * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}. * Otherwise, this set will be ignored. * * If the set is empty, then all channels in 6GHz are allowed. */ private final @NonNull Set mAllowedAcsChannels6g; /** * The maximum channel bandwidth for SoftAp operation * * Default value is SoftApInfo#CHANNEL_WIDTH_AUTO which means the channel bandwidth * is to be selected by the chip based on device capabilities. *

* * Valid values: {@link SoftApInfo#CHANNEL_WIDTH_AUTO}, * {@link SoftApInfo#CHANNEL_WIDTH_20MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_40MHZ}, * {@link SoftApInfo#CHANNEL_WIDTH_80MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_160MHZ}, * {@link SoftApInfo#CHANNEL_WIDTH_320MHZ} * */ private final @WifiAnnotations.Bandwidth int mMaxChannelBandwidth; /** * 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}, * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION}, * {@link #SECURITY_TYPE_WPA3_OWE} */ 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 mBlockedClientList; /** * The list of allowed client that can associate to the AP. */ private final List 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, RANDOMIZATION_NON_PERSISTENT}) public @interface MacRandomizationSetting {} /** * Use the factory MAC address as the BSSID of the AP. * * @hide */ @SystemApi public static final int RANDOMIZATION_NONE = 0; /** * Generate a persistent randomized MAC address as the BSSID of the AP. * The MAC address is persisted per SSID - i.e. as long as the SSID of the AP doesn't change * then it will have a persistent MAC address (which is initially random and is not the factory * MAC address). * * @hide */ @SystemApi public static final int RANDOMIZATION_PERSISTENT = 1; /** * Generate a randomized MAC address as the BSSID of the AP. The MAC address is not persisted * - it is re-generated every time the AP is re-enabled. * @hide */ @SystemApi public static final int RANDOMIZATION_NON_PERSISTENT = 2; /** * 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 802.11be AP is enabled or not. */ private boolean mIeee80211beEnabled; /** * Whether the current configuration is configured by user or not. */ private boolean mIsUserConfiguration; /** * Randomized MAC address to use with this configuration when MAC randomization setting * is {@link #RANDOMIZATION_PERSISTENT}. */ private final @Nullable MacAddress mPersistentRandomizedMacAddress; /** * Delay in milliseconds before shutting down an instance in bridged AP. */ private final long mBridgedModeOpportunisticShutdownTimeoutMillis; /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */ private @NonNull List mVendorData; /** * 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; /** * The definition of security type WPA3-OWE Transition. */ public static final int SECURITY_TYPE_WPA3_OWE_TRANSITION = 4; /** * The definition of security type WPA3-OWE. */ public static final int SECURITY_TYPE_WPA3_OWE = 5; /** @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, SECURITY_TYPE_WPA3_OWE_TRANSITION, SECURITY_TYPE_WPA3_OWE, }) public @interface SecurityType {} /** Private constructor for Builder and Parcelable implementation. */ private SoftApConfiguration( @Nullable WifiSsid ssid, @Nullable MacAddress bssid, @Nullable String passphrase, boolean hiddenSsid, @NonNull SparseIntArray channels, @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, long shutdownTimeoutMillis, boolean clientControlByUser, @NonNull List blockedList, @NonNull List allowedList, int macRandomizationSetting, boolean bridgedModeOpportunisticShutdownEnabled, boolean ieee80211axEnabled, boolean ieee80211beEnabled, boolean isUserConfiguration, long bridgedModeOpportunisticShutdownTimeoutMillis, @NonNull List vendorElements, @Nullable MacAddress persistentRandomizedMacAddress, @NonNull Set allowedAcsChannels24g, @NonNull Set allowedAcsChannels5g, @NonNull Set allowedAcsChannels6g, @WifiAnnotations.Bandwidth int maxChannelBandwidth, @Nullable List vendorData) { mWifiSsid = 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; mIeee80211beEnabled = ieee80211beEnabled; mIsUserConfiguration = isUserConfiguration; mBridgedModeOpportunisticShutdownTimeoutMillis = bridgedModeOpportunisticShutdownTimeoutMillis; mVendorElements = new ArrayList<>(vendorElements); mPersistentRandomizedMacAddress = persistentRandomizedMacAddress; mAllowedAcsChannels2g = new HashSet<>(allowedAcsChannels24g); mAllowedAcsChannels5g = new HashSet<>(allowedAcsChannels5g); mAllowedAcsChannels6g = new HashSet<>(allowedAcsChannels6g); mMaxChannelBandwidth = maxChannelBandwidth; mVendorData = new ArrayList<>(vendorData); } @Override public boolean equals(Object otherObj) { if (this == otherObj) { return true; } if (!(otherObj instanceof SoftApConfiguration)) { return false; } SoftApConfiguration other = (SoftApConfiguration) otherObj; return Objects.equals(mWifiSsid, other.mWifiSsid) && 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 && mIeee80211beEnabled == other.mIeee80211beEnabled && mIsUserConfiguration == other.mIsUserConfiguration && mBridgedModeOpportunisticShutdownTimeoutMillis == other.mBridgedModeOpportunisticShutdownTimeoutMillis && Objects.equals(mVendorElements, other.mVendorElements) && Objects.equals( mPersistentRandomizedMacAddress, other.mPersistentRandomizedMacAddress) && Objects.equals(mAllowedAcsChannels2g, other.mAllowedAcsChannels2g) && Objects.equals(mAllowedAcsChannels5g, other.mAllowedAcsChannels5g) && Objects.equals(mAllowedAcsChannels6g, other.mAllowedAcsChannels6g) && mMaxChannelBandwidth == other.mMaxChannelBandwidth && Objects.equals(mVendorData, other.mVendorData); } @Override public int hashCode() { return Objects.hash( mWifiSsid, mBssid, mPassphrase, mHiddenSsid, mChannels.toString(), mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList, mAllowedClientList, mMacRandomizationSetting, mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled, mIeee80211beEnabled, mIsUserConfiguration, mBridgedModeOpportunisticShutdownTimeoutMillis, mVendorElements, mPersistentRandomizedMacAddress, mAllowedAcsChannels2g, mAllowedAcsChannels5g, mAllowedAcsChannels6g, mMaxChannelBandwidth, mVendorData); } @Override public String toString() { StringBuilder sbuf = new StringBuilder(); sbuf.append("ssid = ").append(mWifiSsid == null ? null : mWifiSsid.toString()); if (mBssid != null) sbuf.append(" \n bssid = ").append(mBssid.toString()); sbuf.append(" \n Passphrase = ").append( TextUtils.isEmpty(mPassphrase) ? "" : ""); 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 BridgedModeOpportunisticShutdownTimeoutMillis = ") .append(mBridgedModeOpportunisticShutdownTimeoutMillis); sbuf.append(" \n Ieee80211axEnabled = ").append(mIeee80211axEnabled); sbuf.append(" \n Ieee80211beEnabled = ").append(mIeee80211beEnabled); sbuf.append(" \n isUserConfiguration = ").append(mIsUserConfiguration); sbuf.append(" \n vendorElements = ").append(mVendorElements); sbuf.append(" \n mPersistentRandomizedMacAddress = ") .append(mPersistentRandomizedMacAddress); sbuf.append(" \n mAllowedAcsChannels2g = ").append(mAllowedAcsChannels2g); sbuf.append(" \n mAllowedAcsChannels5g = ").append(mAllowedAcsChannels5g); sbuf.append(" \n mAllowedAcsChannels6g = ").append(mAllowedAcsChannels6g); sbuf.append(" \n mMaxChannelBandwidth = ").append(mMaxChannelBandwidth); sbuf.append(" \n mVendorData = ").append(mVendorData); return sbuf.toString(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mWifiSsid, 0); 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(mIeee80211beEnabled); dest.writeBoolean(mIsUserConfiguration); dest.writeLong(mBridgedModeOpportunisticShutdownTimeoutMillis); dest.writeTypedList(mVendorElements); dest.writeParcelable(mPersistentRandomizedMacAddress, flags); writeHashSetInt(dest, mAllowedAcsChannels2g); writeHashSetInt(dest, mAllowedAcsChannels5g); writeHashSetInt(dest, mAllowedAcsChannels6g); dest.writeInt(mMaxChannelBandwidth); dest.writeList(mVendorData); } /* 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; } /* Write HashSet into Parcel */ private static void writeHashSetInt(@NonNull Parcel dest, @NonNull Set set) { if (set.isEmpty()) { dest.writeInt(-1); return; } dest.writeInt(set.size()); for (int val : set) { dest.writeInt(val); } } /* Read HashSet from Parcel */ @NonNull private static Set readHashSetInt(@NonNull Parcel in) { Set set = new HashSet<>(); int len = in.readInt(); if (len < 0) { return set; } for (int i = 0; i < len; i++) { set.add(in.readInt()); } return set; } /* Read List from Parcel */ @NonNull private static List readOuiKeyedDataList(@NonNull Parcel in) { List dataList = new ArrayList<>(); if (SdkLevel.isAtLeastT()) { in.readList(dataList, OuiKeyedData.class.getClassLoader(), OuiKeyedData.class); } else { in.readList(dataList, OuiKeyedData.class.getClassLoader()); } return dataList; } @Override public int describeContents() { return 0; } @NonNull public static final Creator CREATOR = new Creator() { @Override public SoftApConfiguration createFromParcel(Parcel in) { return new SoftApConfiguration( in.readParcelable(WifiSsid.class.getClassLoader()), 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(), in.readBoolean(), in.readLong(), in.createTypedArrayList(ScanResult.InformationElement.CREATOR), in.readParcelable(MacAddress.class.getClassLoader()), readHashSetInt(in), readHashSetInt(in), readHashSetInt(in), in.readInt(), readOuiKeyedDataList(in)); } @Override public SoftApConfiguration[] newArray(int size) { return new SoftApConfiguration[size]; } }; /** * Return the UTF-8 String set to be the SSID for the AP. If the SSID cannot be decoded as * UTF-8, then this will return {@link WifiManager#UNKNOWN_SSID}. * * @deprecated Use {@link #getWifiSsid()} instead. */ @Nullable @Deprecated public String getSsid() { if (mWifiSsid == null) { return null; } CharSequence utf8Text = mWifiSsid.getUtf8Text(); return utf8Text != null ? utf8Text.toString() : WifiManager.UNKNOWN_SSID; } /** * Return WifiSsid set to be the SSID for the AP. */ @Nullable public WifiSsid getWifiSsid() { return mWifiSsid; } /** * Return VendorElements for the AP. * @hide */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi public List getVendorElements() { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } return getVendorElementsInternal(); } /** * @see #getVendorElements() * @hide */ public List getVendorElementsInternal() { return new ArrayList<>(mVendorElements); } /** * Returns MAC address set to be BSSID for the AP. */ @Nullable public MacAddress getBssid() { return mBssid; } /** * Returns String set to be passphrase for current AP. */ @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. */ 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}, * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION}, * {@link #SECURITY_TYPE_WPA3_OWE} */ 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() { if (!CompatChanges.isChangeEnabled( REMOVE_ZERO_FOR_TIMEOUT_SETTING) && mShutdownTimeoutMillis == DEFAULT_TIMEOUT) { // For legacy application, return 0 when setting is DEFAULT_TIMEOUT. return 0; } 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 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 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 Soft AP is configured to enable 802.11be. * This is an indication that if the device support 802.11be AP then to enable or disable * that feature. If the device does not support 802.11be AP then this flag is ignored. * See also {@link Builder#setIeee80211beEnabled(boolean}} * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi public boolean isIeee80211beEnabled() { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } return mIeee80211beEnabled; } /** * Returns the allowed channels for ACS in a selected band. * * If an empty array is returned, then all channels in that band are allowed * The channels are configured using {@link Builder#setAllowedAcsChannels(int, int[])} * * @param band one of the following band types: * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. * * @return array of the allowed channels for ACS in that band * * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull @SystemApi public int[] getAllowedAcsChannels(@BandType int band) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } switch(band) { case BAND_2GHZ: return mAllowedAcsChannels2g.stream().mapToInt(Integer::intValue).toArray(); case BAND_5GHZ: return mAllowedAcsChannels5g.stream().mapToInt(Integer::intValue).toArray(); case BAND_6GHZ: return mAllowedAcsChannels6g.stream().mapToInt(Integer::intValue).toArray(); default: throw new IllegalArgumentException("getAllowedAcsChannels: Invalid band: " + band); } } /** * Returns configured maximum channel bandwidth for the SoftAp connection. * * If not configured, it will return {@link SoftApInfo#CHANNEL_WIDTH_AUTO} * * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi public @WifiAnnotations.Bandwidth int getMaxChannelBandwidth() { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } return mMaxChannelBandwidth; } /** * Returns whether or not the {@link SoftApConfiguration} was configured by the user * (as opposed to the default system configuration). *

* 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(); } /** * Returns the randomized MAC address to be used by this configuration. * * The Soft AP may be configured to use a persistent randomized MAC address with * {@link Builder#setMacRandomizationSetting(int)}. This method returns the persistent * randomized MAC address which will be used for the Soft AP controlled by this configuration. * * @hide */ @SystemApi public @NonNull MacAddress getPersistentRandomizedMacAddress() { return mPersistentRandomizedMacAddress; } /** * @hide */ public boolean isUserConfigurationInternal() { return mIsUserConfiguration; } /** * Returns the bridged mode opportunistic shutdown timeout in milliseconds. * An instance in bridged AP will shutdown when there is no device associated to it for * the timeout duration. See also * {@link Builder#setBridgedModeOpportunisticShutdownTimeoutMillis(long)}. * * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi public long getBridgedModeOpportunisticShutdownTimeoutMillis() { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } return mBridgedModeOpportunisticShutdownTimeoutMillis; } /** * @hide */ public long getBridgedModeOpportunisticShutdownTimeoutMillisInternal() { return mBridgedModeOpportunisticShutdownTimeoutMillis; } /** * Return the vendor-provided configuration data, if it exists. See also {@link * Builder#setVendorData(List)} * * @return Vendor configuration data, or empty list if it does not exist. * @hide */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @NonNull @SystemApi public List getVendorData() { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } return mVendorData; } /** * 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. * * To maintain legacy behavior, the SSID of the WifiConfiguration will be the UTF-8 * representation of the SSID without double quotes, as opposed to the double-quoted UTF-8 * format documented in {@link WifiConfiguration#SSID}. If the SSID cannot be decoded as UTF-8, * then the SSID of the WifiConfiguration will be {@link WifiManager#UNKNOWN_SSID}. * *

  • SoftAp band in {@link WifiConfiguration.apBand} only supports * 2GHz, 5GHz, 2GHz+5GHz bands, so conversion is limited to these bands.
  • * *
  • SoftAp security type in {@link WifiConfiguration.KeyMgmt} only supports * NONE, WPA2_PSK, so conversion is limited to these security type.
  • * @hide */ @Nullable @SystemApi public WifiConfiguration toWifiConfiguration() { WifiConfiguration wifiConfig = new WifiConfiguration(); CharSequence utf8Text = mWifiSsid != null ? mWifiSsid.getUtf8Text() : null; wifiConfig.SSID = utf8Text != null ? utf8Text.toString() : WifiManager.UNKNOWN_SSID; 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 WifiSsid mWifiSsid; 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 mBlockedClientList; private List mAllowedClientList; private int mMacRandomizationSetting; private boolean mBridgedModeOpportunisticShutdownEnabled; private boolean mIeee80211axEnabled; private boolean mIeee80211beEnabled; private boolean mIsUserConfiguration; private long mBridgedModeOpportunisticShutdownTimeoutMillis; private List mVendorElements; private MacAddress mPersistentRandomizedMacAddress; private Set mAllowedAcsChannels2g; private Set mAllowedAcsChannels5g; private Set mAllowedAcsChannels6g; private @WifiAnnotations.Bandwidth int mMaxChannelBandwidth; private @Nullable List mVendorData; /** * Constructs a Builder with default values (see {@link Builder}). */ public Builder() { mWifiSsid = 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 = DEFAULT_TIMEOUT; mClientControlByUser = false; mBlockedClientList = new ArrayList<>(); mAllowedClientList = new ArrayList<>(); if (SdkLevel.isAtLeastT()) { mMacRandomizationSetting = RANDOMIZATION_NON_PERSISTENT; } else { mMacRandomizationSetting = RANDOMIZATION_PERSISTENT; } mBridgedModeOpportunisticShutdownEnabled = true; mIeee80211axEnabled = true; mIeee80211beEnabled = true; mIsUserConfiguration = true; mBridgedModeOpportunisticShutdownTimeoutMillis = DEFAULT_TIMEOUT; mVendorElements = new ArrayList<>(); mPersistentRandomizedMacAddress = null; mAllowedAcsChannels2g = new HashSet<>(); mAllowedAcsChannels5g = new HashSet<>(); mAllowedAcsChannels6g = new HashSet<>(); mMaxChannelBandwidth = SoftApInfo.CHANNEL_WIDTH_AUTO; mVendorData = new ArrayList<>(); } /** * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance. */ public Builder(@NonNull SoftApConfiguration other) { if (other == null) { Log.e(TAG, "Cannot provide a null SoftApConfiguration"); return; } mWifiSsid = other.mWifiSsid; 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; mIeee80211beEnabled = other.mIeee80211beEnabled; mIsUserConfiguration = other.mIsUserConfiguration; mBridgedModeOpportunisticShutdownTimeoutMillis = other.mBridgedModeOpportunisticShutdownTimeoutMillis; mVendorElements = new ArrayList<>(other.mVendorElements); mPersistentRandomizedMacAddress = other.mPersistentRandomizedMacAddress; mAllowedAcsChannels2g = new HashSet<>(other.mAllowedAcsChannels2g); mAllowedAcsChannels5g = new HashSet<>(other.mAllowedAcsChannels5g); mAllowedAcsChannels6g = new HashSet<>(other.mAllowedAcsChannels6g); mMaxChannelBandwidth = other.mMaxChannelBandwidth; if (SdkLevel.isAtLeastS() && mBssid != null) { // Auto set correct MAC randomization setting for the legacy SoftApConfiguration // to avoid the exception happen when framework (system server) copy // SoftApConfiguration. mMacRandomizationSetting = RANDOMIZATION_NONE; } mVendorData = new ArrayList<>(other.mVendorData); } /** * 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"); } } // mMacRandomizationSetting supported from S. if (SdkLevel.isAtLeastS() && CompatChanges.isChangeEnabled( FORCE_MUTUAL_EXCLUSIVE_BSSID_MAC_RAMDONIZATION_SETTING) && mBssid != null && mMacRandomizationSetting != RANDOMIZATION_NONE) { throw new IllegalArgumentException("A BSSID had configured but MAC randomization" + " setting is not NONE"); } if (!CompatChanges.isChangeEnabled( REMOVE_ZERO_FOR_TIMEOUT_SETTING) && mShutdownTimeoutMillis == DEFAULT_TIMEOUT) { mShutdownTimeoutMillis = 0; // Use 0 for legacy app. } return new SoftApConfiguration( mWifiSsid, mBssid, mPassphrase, mHiddenSsid, mChannels, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList, mAllowedClientList, mMacRandomizationSetting, mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled, mIeee80211beEnabled, mIsUserConfiguration, mBridgedModeOpportunisticShutdownTimeoutMillis, mVendorElements, mPersistentRandomizedMacAddress, mAllowedAcsChannels2g, mAllowedAcsChannels5g, mAllowedAcsChannels6g, mMaxChannelBandwidth, mVendorData); } /** * Specifies a UTF-8 SSID for the AP. *

    * Null SSID only support when configure a local-only hotspot. *

    *

  • If not set, defaults to null.
  • * * @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, not unicode, or if the byte * representation is longer than 32 bytes. * * @deprecated Use {@link #setWifiSsid(WifiSsid)} instead. */ @NonNull @Deprecated public Builder setSsid(@Nullable String ssid) { if (ssid == null) { mWifiSsid = null; return this; } Preconditions.checkStringNotEmpty(ssid); Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid)); mWifiSsid = WifiSsid.fromUtf8Text(ssid); return this; } /** * Specifies an SSID for the AP in the form of WifiSsid. *

    * Null SSID only support when configure a local-only hotspot. *

    *

  • If not set, defaults to null.
  • * * @param wifiSsid SSID, or null ot have the SSID automatically chosen by the framework. * @return Builder for chaining. */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) public Builder setWifiSsid(@Nullable WifiSsid wifiSsid) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } mWifiSsid = wifiSsid; return this; } /** * Specify vendor-specific information elements for the (Soft) AP to transmit in its beacons * and probe responses. Method also validates the structure and throws * IllegalArgumentException in cases when ID of IE is not 0xDD (221) or incoming list * contain duplicate elements. * * @param vendorElements VendorElements * @return Builder for chaining. */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) public Builder setVendorElements( @NonNull List vendorElements) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } for (ScanResult.InformationElement e : vendorElements) { if (e.id != ScanResult.InformationElement.EID_VSA) { throw new IllegalArgumentException("received InformationElement which is not " + "related to VendorElements. VendorElement block should start with " + HexEncoding.encodeToString( new byte[]{ (byte) ScanResult.InformationElement.EID_VSA })); } } final HashSet set = new HashSet<>(vendorElements); if (set.size() < vendorElements.size()) { throw new IllegalArgumentException("vendor elements array contain duplicates. " + "Please avoid passing duplicated and keep structure clean."); } mVendorElements = new ArrayList<>(vendorElements); return this; } /** * Specifies a BSSID for the AP. *

    *

  • If not set, defaults to null.
  • * * When this method is called, the caller needs to configure MAC randomization settings to * {@link #RANDOMIZATION_NONE}. See {@link #setMacRandomizationSetting(int)} for details. * * 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): *
  • MAC address 1: copy value of MAC address, * and set byte 1 = (0xFF - BSSID[1])
  • *
  • MAC address 2: copy value of MAC address, * and set byte 2 = (0xFF - BSSID[2])
  • * * 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 * *

    * 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}, * {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION}, * {@link #SECURITY_TYPE_WPA3_OWE}. * @param passphrase The passphrase to use for sepcific {@code securityType} configuration * or null with {@link #SECURITY_TYPE_OPEN}, {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION}, * and {@link #SECURITY_TYPE_WPA3_OWE}. * * @return Builder for chaining. * @throws IllegalArgumentException when the passphrase is non-null for * - {@link #SECURITY_TYPE_OPEN} * - {@link #SECURITY_TYPE_WPA3_OWE_TRANSITION} * - {@link #SECURITY_TYPE_WPA3_OWE} * @throws IllegalArgumentException when the passphrase is empty for * - {@link #SECURITY_TYPE_WPA2_PSK}, * - {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION}, * - {@link #SECURITY_TYPE_WPA3_SAE}, * @throws IllegalArgumentException before {@link android.os.Build.VERSION_CODES#TIRAMISU}) * when the passphrase is not between 8 and 63 bytes (inclusive) for * - {@link #SECURITY_TYPE_WPA2_PSK} * - {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION} */ @NonNull public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) { if (!SdkLevel.isAtLeastT() && (securityType == SECURITY_TYPE_WPA3_OWE_TRANSITION || securityType == SECURITY_TYPE_WPA3_OWE)) { throw new UnsupportedOperationException(); } if (securityType == SECURITY_TYPE_OPEN || securityType == SECURITY_TYPE_WPA3_OWE_TRANSITION || securityType == SECURITY_TYPE_WPA3_OWE) { if (passphrase != null) { throw new IllegalArgumentException( "passphrase should be null when security type is open"); } } else { Preconditions.checkStringNotEmpty(passphrase); if (!SdkLevel.isAtLeastT() && (securityType == SECURITY_TYPE_WPA2_PSK || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION)) { int passphraseByteLength = 0; if (!TextUtils.isEmpty(passphrase)) { passphraseByteLength = passphrase.getBytes(StandardCharsets.UTF_8).length; } if (passphraseByteLength < PSK_MIN_LEN || passphraseByteLength > PSK_MAX_LEN) { throw new IllegalArgumentException( "Passphrase length 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). *

    *

  • If not set, defaults to false (i.e not a hidden network).
  • * * @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. *

    *

  • If not set, defaults to {@link #BAND_2GHZ}.
  • * * @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). *

    * * 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. * *

    * 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. * *

    * @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. * *

    * 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. * *

    * @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). *

    * 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). * *

    *

  • If not set, defaults to 0.
  • * * 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}. * *

    * 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. * *

    *

  • If not set, defaults to true
  • * * @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 {@link #DEFAULT_TIMEOUT} to have the framework automatically use * default timeout setting which defined in * {@link R.integer.config_wifi_framework_soft_ap_timeout_delay} * *

    *

  • If not set, defaults to {@link #DEFAULT_TIMEOUT}
  • *
  • The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is * set to true
  • * * @param timeoutMillis milliseconds of the timeout delay. Any value less than 1 is invalid * except {@link #DEFAULT_TIMEOUT}. * @return Builder for chaining. * * @see #setAutoShutdownEnabled(boolean) */ @NonNull public Builder setShutdownTimeoutMillis(@IntRange(from = -1) long timeoutMillis) { if (CompatChanges.isChangeEnabled( REMOVE_ZERO_FOR_TIMEOUT_SETTING) && timeoutMillis < 1) { if (timeoutMillis != DEFAULT_TIMEOUT) { throw new IllegalArgumentException("Invalid timeout value: " + timeoutMillis); } } else if (timeoutMillis < 0) { throw new IllegalArgumentException("Invalid timeout value from legacy app: " + timeoutMillis); } 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)}. * *

    * 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} * *

    * 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}. * *

    *

  • If not set, defaults to false (i.e The authoriztion is not required).
  • * * @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; } /** * Configures the set of channel numbers in the specified band that are allowed * to be selected by the Automatic Channel Selection (ACS) algorithm. *

    * * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}. * Otherwise, these sets will be ignored. *

    * * @param band one of the following band types: * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. * * @param channels that are allowed to be used by ACS algorithm in this band. If it is * configured to an empty array or not configured, then all channels within that band * will be allowed. *

    * * @return Builder for chaining. */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) public Builder setAllowedAcsChannels(@BandType int band, @NonNull int[] channels) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (channels == null) { throw new IllegalArgumentException( "Passing a null object to setAllowedAcsChannels"); } if ((band != BAND_2GHZ) && (band != BAND_5GHZ) && (band != BAND_6GHZ)) { throw new IllegalArgumentException( "Passing an invalid band to setAllowedAcsChannels"); } for (int channel : channels) { if (!isChannelBandPairValid(channel, band)) { throw new IllegalArgumentException( "Invalid channel to setAllowedAcsChannels: band: " + band + "channel: " + channel); } } HashSet set = IntStream.of(channels).boxed() .collect(Collectors.toCollection(HashSet::new)); switch(band) { case BAND_2GHZ: mAllowedAcsChannels2g = set; break; case BAND_5GHZ: mAllowedAcsChannels5g = set; break; case BAND_6GHZ: mAllowedAcsChannels6g = set; break; } return this; } /** * Sets maximum channel bandwidth for the SoftAp Connection * * If not set, the SoftAp connection will seek the maximum channel bandwidth achievable on * the device. However, in some cases the caller will need to put a cap on the channel * bandwidth through this API. * * @param maxChannelBandwidth one of {@link SoftApInfo#CHANNEL_WIDTH_AUTO}, * {@link SoftApInfo#CHANNEL_WIDTH_20MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_40MHZ}, * {@link SoftApInfo#CHANNEL_WIDTH_80MHZ}, {@link SoftApInfo#CHANNEL_WIDTH_160MHZ}, * or {@link SoftApInfo#CHANNEL_WIDTH_320MHZ} * * @return builder for chaining */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) public Builder setMaxChannelBandwidth(@WifiAnnotations.Bandwidth int maxChannelBandwidth) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } switch (maxChannelBandwidth) { case SoftApInfo.CHANNEL_WIDTH_AUTO: case SoftApInfo.CHANNEL_WIDTH_20MHZ: case SoftApInfo.CHANNEL_WIDTH_40MHZ: case SoftApInfo.CHANNEL_WIDTH_80MHZ: case SoftApInfo.CHANNEL_WIDTH_160MHZ: case SoftApInfo.CHANNEL_WIDTH_320MHZ: mMaxChannelBandwidth = maxChannelBandwidth; break; default: throw new IllegalArgumentException( "Invalid channel bandwidth value(" + maxChannelBandwidth + ") configured"); } 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: *

      *
    • Rejected
    • *
    • A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)} * is issued (which allows the user to add them to the allowed client list if desired).
    • *
    * * @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 allowedClientList) { mAllowedClientList = new ArrayList<>(allowedClientList); return this; } /** * This API configures the list of clients which are blocked and cannot associate * to the Soft AP. * *

    * 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} * *

    * 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 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} or * {@link #RANDOMIZATION_NON_PERSISTENT}. When this method is called with * {@link #RANDOMIZATION_PERSISTENT} or {@link #RANDOMIZATION_NON_PERSISTENT}, the caller * the caller must not call {@link #setBssid(MacAddress)}. * *

    *

  • If not set, defaults to {@link #RANDOMIZATION_NON_PERSISTENT}
  • * *

    * Requires HAL support when set to {@link #RANDOMIZATION_PERSISTENT} or * {@link #RANDOMIZATION_NON_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}, {@link #RANDOMIZATION_PERSISTENT} or * {@link #RANDOMIZATION_NON_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. * *

    * 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. *

    * * 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. * *

    * 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. * *

    *

  • If not set, defaults to true
  • * * @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. * *

    * 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. *

    *

  • If not set, defaults to true - which will be ignored on devices * which do not support 802.11ax
  • * * @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 to enable 802.11be on the Soft AP. * *

    * Note: Only relevant when the device supports 802.11be on the Soft AP. * If enabled on devices that do not support 802.11be then ignored. * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and * {@link SoftApCapability#areFeaturesSupported(long)} * with {@link SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE} to determine * whether or not 802.11be is supported on the Soft AP. *

    *

  • If not set, defaults to true - which will be ignored on devices * which do not support 802.11be
  • * * @param enable true to enable, false to disable. * @return Builder for chaining. * */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull public Builder setIeee80211beEnabled(boolean enable) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } mIeee80211beEnabled = 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; } /** * Specifies bridged mode opportunistic shutdown timeout in milliseconds. * An instance of bridged Soft AP will shut down when there is no device connected to it * for this timeout duration. * * Specify a value of {@link DEFAULT_TIMEOUT} to have the framework automatically use * default timeout setting defined by * {@link * R.integer.config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond} * *

    *

  • If not set, defaults to {@link #DEFAULT_TIMEOUT}
  • *
  • The shut down timeout will apply when * {@link #setBridgedModeOpportunisticShutdownEnabled(boolean)} is set to true
  • * * @param timeoutMillis milliseconds of the timeout delay. Any value less than 1 is invalid * except {@link #DEFAULT_TIMEOUT}. * @return Builder for chaining. * * @see #setBridgedModeOpportunisticShutdownEnabled(boolean) */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull public Builder setBridgedModeOpportunisticShutdownTimeoutMillis( @IntRange(from = -1) long timeoutMillis) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (timeoutMillis < 1 && timeoutMillis != DEFAULT_TIMEOUT) { throw new IllegalArgumentException("Invalid timeout value: " + timeoutMillis); } mBridgedModeOpportunisticShutdownTimeoutMillis = timeoutMillis; return this; } /** * @param mac persistent randomized MacAddress generated by the frameworks. * @hide */ @NonNull public Builder setRandomizedMacAddress(@NonNull MacAddress mac) { if (mac == null) { throw new IllegalArgumentException("setRandomizedMacAddress received" + " null MacAddress."); } mPersistentRandomizedMacAddress = mac; return this; } /** * Set additional vendor-provided configuration data. * * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided * configuration data. Note that multiple elements with the same OUI are allowed. * @return Builder for chaining. */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @NonNull public Builder setVendorData(@NonNull List vendorData) { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } if (vendorData == null) { throw new IllegalArgumentException("setVendorData received a null value"); } mVendorData = vendorData; return this; } } }