| /* |
| * 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.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 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 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; |
| |
| /** |
| * 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<ScanResult.InformationElement> 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<Integer> 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<Integer> 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<Integer> 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. |
| * <p> |
| * |
| * 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<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, |
| 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; |
| |
| /** |
| * 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<MacAddress> blockedList, @NonNull List<MacAddress> allowedList, |
| int macRandomizationSetting, boolean bridgedModeOpportunisticShutdownEnabled, |
| boolean ieee80211axEnabled, boolean ieee80211beEnabled, boolean isUserConfiguration, |
| long bridgedModeOpportunisticShutdownTimeoutMillis, |
| @NonNull List<ScanResult.InformationElement> vendorElements, |
| @Nullable MacAddress persistentRandomizedMacAddress, |
| @NonNull Set<Integer> allowedAcsChannels24g, @NonNull Set<Integer> allowedAcsChannels5g, |
| @NonNull Set<Integer> allowedAcsChannels6g, |
| @WifiAnnotations.Bandwidth int maxChannelBandwidth) { |
| 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; |
| } |
| |
| @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; |
| } |
| |
| @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); |
| } |
| |
| @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) ? "<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 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); |
| 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); |
| } |
| |
| /* 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<Integer> into Parcel */ |
| private static void writeHashSetInt(@NonNull Parcel dest, @NonNull Set<Integer> set) { |
| if (set.isEmpty()) { |
| dest.writeInt(-1); |
| return; |
| } |
| |
| dest.writeInt(set.size()); |
| for (int val : set) { |
| dest.writeInt(val); |
| } |
| } |
| |
| /* Read HashSet<Integer> from Parcel */ |
| @NonNull |
| private static Set<Integer> readHashSetInt(@NonNull Parcel in) { |
| Set<Integer> 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; |
| } |
| |
| @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.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()); |
| } |
| |
| @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} |
| * See also {@link Builder#setSsid(String)}. |
| * |
| * @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. |
| * See also {@link Builder#setWifiSsid(WifiSsid)}. |
| */ |
| @Nullable |
| public WifiSsid getWifiSsid() { |
| return mWifiSsid; |
| } |
| |
| /** |
| * Return VendorElements for the AP. |
| * @hide |
| */ |
| @NonNull |
| @RequiresApi(Build.VERSION_CODES.TIRAMISU) |
| @SystemApi |
| public List<ScanResult.InformationElement> getVendorElements() { |
| if (!SdkLevel.isAtLeastT()) { |
| throw new UnsupportedOperationException(); |
| } |
| return getVendorElementsInternal(); |
| } |
| |
| /** |
| * @see #getVendorElements() |
| * @hide |
| */ |
| public List<ScanResult.InformationElement> getVendorElementsInternal() { |
| return new ArrayList<>(mVendorElements); |
| } |
| |
| /** |
| * 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}, |
| * {@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<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(); |
| } |
| |
| /** |
| * @see #isIeee80211beEnabled() |
| * @hide |
| */ |
| public boolean isIeee80211beEnabledInternal() { |
| return mIeee80211beEnabled; |
| } |
| |
| /** |
| * 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 isIeee80211beEnabledInternal(); |
| } |
| |
| /** |
| * 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). |
| * <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(); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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}. |
| * |
| * <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(); |
| 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<MacAddress> mBlockedClientList; |
| private List<MacAddress> mAllowedClientList; |
| private int mMacRandomizationSetting; |
| private boolean mBridgedModeOpportunisticShutdownEnabled; |
| private boolean mIeee80211axEnabled; |
| private boolean mIeee80211beEnabled; |
| private boolean mIsUserConfiguration; |
| private long mBridgedModeOpportunisticShutdownTimeoutMillis; |
| private List<ScanResult.InformationElement> mVendorElements; |
| private MacAddress mPersistentRandomizedMacAddress; |
| private Set<Integer> mAllowedAcsChannels2g; |
| private Set<Integer> mAllowedAcsChannels5g; |
| private Set<Integer> mAllowedAcsChannels6g; |
| private @WifiAnnotations.Bandwidth int mMaxChannelBandwidth; |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Specifies a UTF-8 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, 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. |
| * <p> |
| * Null SSID only support when configure a local-only hotspot. |
| * <p> |
| * <li>If not set, defaults to null.</li> |
| * |
| * @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<ScanResult.InformationElement> 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<ScanResult.InformationElement> 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. |
| * <p> |
| * <li>If not set, defaults to null.</li> |
| * |
| * 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): |
| * <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}, |
| * {@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). |
| * <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 {@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} |
| * |
| * <p> |
| * <li>If not set, defaults to {@link #DEFAULT_TIMEOUT}</li> |
| * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is |
| * set to true</li> |
| * |
| * @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)}. |
| * |
| * <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; |
| } |
| |
| /** |
| * Configures the set of channel numbers in the specified band that are allowed |
| * to be selected by the Automatic Channel Selection (ACS) algorithm. |
| * <p> |
| * |
| * Requires the driver to support {@link SoftApCapability#SOFTAP_FEATURE_ACS_OFFLOAD}. |
| * Otherwise, these sets will be ignored. |
| * <p> |
| * |
| * @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. |
| * <p> |
| * |
| * @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<Integer> 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: |
| * <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} 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)}. |
| * |
| * <p> |
| * <li>If not set, defaults to {@link #RANDOMIZATION_NON_PERSISTENT}</li> |
| * |
| * <p> |
| * 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. |
| * |
| * <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 to enable 802.11be on the Soft AP. |
| * |
| * <p> |
| * 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. |
| * <p> |
| * <li>If not set, defaults to true - which will be ignored on devices |
| * which do not support 802.11be</li> |
| * |
| * @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} |
| * |
| * <p> |
| * <li>If not set, defaults to {@link #DEFAULT_TIMEOUT}</li> |
| * <li>The shut down timeout will apply when |
| * {@link #setBridgedModeOpportunisticShutdownEnabled(boolean)} is set to true</li> |
| * |
| * @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; |
| } |
| } |
| } |