Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d26a9f75 authored by lesl's avatar lesl
Browse files

softap: Add blocked/allowed client list support

Bug: 142752869
Test: atest frameworks/base/wifi/tests/
Test: Manuel test, enable softap with feature enable. check client
connect log and use test code to simulate the real use case.
Change-Id: I05b0c0d376ec8437556a66e372c2693b5ce5f266
parent fa833b69
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -5831,7 +5831,9 @@ package android.net.wifi {
  public final class SoftApConfiguration implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.net.MacAddress> getAllowedClientList();
    method public int getBand();
    method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList();
    method @Nullable public android.net.MacAddress getBssid();
    method public int getChannel();
    method public int getMaxNumberOfClients();
@@ -5839,6 +5841,7 @@ package android.net.wifi {
    method public int getSecurityType();
    method public int getShutdownTimeoutMillis();
    method @Nullable public String getSsid();
    method public boolean isClientControlByUserEnabled();
    method public boolean isHiddenSsid();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field public static final int BAND_2GHZ = 1; // 0x1
@@ -5856,9 +5859,11 @@ package android.net.wifi {
    ctor public SoftApConfiguration.Builder();
    ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
    method @NonNull public android.net.wifi.SoftApConfiguration build();
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
@@ -6097,6 +6102,8 @@ package android.net.wifi {
    field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff
    field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
    field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
    field public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; // 0x0
    field public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; // 0x1
    field public static final int SAP_START_FAILURE_GENERAL = 0; // 0x0
    field public static final int SAP_START_FAILURE_NO_CHANNEL = 1; // 0x1
    field public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; // 0x2
@@ -6138,6 +6145,7 @@ package android.net.wifi {
  }
  public static interface WifiManager.SoftApCallback {
    method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int);
    method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
    method public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>);
    method public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo);
+9 −1
Original line number Diff line number Diff line
@@ -55,9 +55,17 @@ oneway interface ISoftApCallback


    /**
     * Service to manager callback providing information of softap.
     * Service to manager callback providing capability of softap.
     *
     * @param capability is the softap capability. {@link SoftApCapability}
     */
    void onCapabilityChanged(in SoftApCapability capability);

    /**
     * Service to manager callback providing blocked client of softap with specific reason code.
     *
     * @param client the currently blocked client.
     * @param blockedReason one of blocked reason from {@link WifiManager.SapClientBlockedReason}
     */
    void onBlockedClientConnecting(in WifiClient client, int blockedReason);
}
+156 −5
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;

@@ -181,6 +184,22 @@ public final class SoftApConfiguration implements Parcelable {
     */
    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;

    /**
     * Delay in milliseconds before shutting down soft AP when
     * there are no connected devices.
@@ -219,7 +238,9 @@ public final class SoftApConfiguration implements Parcelable {
    /** Private constructor for Builder and Parcelable implementation. */
    private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
            @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
            @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis) {
            @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis,
            boolean clientControlByUser, @NonNull List<MacAddress> blockedList,
            @NonNull List<MacAddress> allowedList) {
        mSsid = ssid;
        mBssid = bssid;
        mPassphrase = passphrase;
@@ -229,6 +250,9 @@ public final class SoftApConfiguration implements Parcelable {
        mSecurityType = securityType;
        mMaxNumberOfClients = maxNumberOfClients;
        mShutdownTimeoutMillis = shutdownTimeoutMillis;
        mClientControlByUser = clientControlByUser;
        mBlockedClientList = new ArrayList<>(blockedList);
        mAllowedClientList = new ArrayList<>(allowedList);
    }

    @Override
@@ -248,13 +272,17 @@ public final class SoftApConfiguration implements Parcelable {
                && mChannel == other.mChannel
                && mSecurityType == other.mSecurityType
                && mMaxNumberOfClients == other.mMaxNumberOfClients
                && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis;
                && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
                && mClientControlByUser == other.mClientControlByUser
                && Objects.equals(mBlockedClientList, other.mBlockedClientList)
                && Objects.equals(mAllowedClientList, other.mAllowedClientList);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis);
                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis,
                mClientControlByUser, mBlockedClientList, mAllowedClientList);
    }

    @Override
@@ -270,6 +298,9 @@ public final class SoftApConfiguration implements Parcelable {
        sbuf.append(" \n SecurityType=").append(getSecurityType());
        sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
        sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
        sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
        sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
        sbuf.append(" \n AllowedClientList=").append(mAllowedClientList);
        return sbuf.toString();
    }

@@ -284,6 +315,9 @@ public final class SoftApConfiguration implements Parcelable {
        dest.writeInt(mSecurityType);
        dest.writeInt(mMaxNumberOfClients);
        dest.writeInt(mShutdownTimeoutMillis);
        dest.writeBoolean(mClientControlByUser);
        dest.writeTypedList(mBlockedClientList);
        dest.writeTypedList(mAllowedClientList);
    }

    @Override
@@ -299,7 +333,9 @@ public final class SoftApConfiguration implements Parcelable {
                    in.readString(),
                    in.readParcelable(MacAddress.class.getClassLoader()),
                    in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
                    in.readInt(), in.readInt());
                    in.readInt(), in.readInt(), in.readBoolean(),
                    in.createTypedArrayList(MacAddress.CREATOR),
                    in.createTypedArrayList(MacAddress.CREATOR));
        }

        @Override
@@ -386,6 +422,34 @@ public final class SoftApConfiguration implements Parcelable {
        return mShutdownTimeoutMillis;
    }

    /**
     * Returns a flag indicating whether clients need to be pre-approved by the user.
     * (true: authorization required) or not (false: not required).
     * {@link Builder#enableClientControlByUser(Boolean)}.
     */
    public boolean isClientControlByUserEnabled() {
        return mClientControlByUser;
    }

    /**
     * Returns List of clients which aren't allowed to associate to the AP.
     *
     * Clients are configured using {@link Builder#setClientList(List, List)}
     */
    @NonNull
    public List<MacAddress> getBlockedClientList() {
        return mBlockedClientList;
    }

    /**
     * List of clients which are allowed to associate to the AP.
     * Clients are configured using {@link Builder#setClientList(List, List)}
     */
    @NonNull
    public List<MacAddress> getAllowedClientList() {
        return mAllowedClientList;
    }

    /**
     * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
     * Soft AP.
@@ -403,6 +467,9 @@ public final class SoftApConfiguration implements Parcelable {
        private int mMaxNumberOfClients;
        private int mSecurityType;
        private int mShutdownTimeoutMillis;
        private boolean mClientControlByUser;
        private List<MacAddress> mBlockedClientList;
        private List<MacAddress> mAllowedClientList;

        /**
         * Constructs a Builder with default values (see {@link Builder}).
@@ -417,6 +484,9 @@ public final class SoftApConfiguration implements Parcelable {
            mMaxNumberOfClients = 0;
            mSecurityType = SECURITY_TYPE_OPEN;
            mShutdownTimeoutMillis = 0;
            mClientControlByUser = false;
            mBlockedClientList = new ArrayList<>();
            mAllowedClientList = new ArrayList<>();
        }

        /**
@@ -434,6 +504,9 @@ public final class SoftApConfiguration implements Parcelable {
            mMaxNumberOfClients = other.mMaxNumberOfClients;
            mSecurityType = other.mSecurityType;
            mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
            mClientControlByUser = other.mClientControlByUser;
            mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
            mAllowedClientList = new ArrayList<>(other.mAllowedClientList);
        }

        /**
@@ -445,7 +518,8 @@ public final class SoftApConfiguration implements Parcelable {
        public SoftApConfiguration build() {
            return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
                    mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
                    mShutdownTimeoutMillis);
                    mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
                    mAllowedClientList);
        }

        /**
@@ -662,5 +736,82 @@ public final class SoftApConfiguration implements Parcelable {
            mShutdownTimeoutMillis = timeoutMillis;
            return this;
        }

        /**
         * Configure the Soft AP to require manual user control of client association.
         * If disabled (the default) then any client 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 #setClientList(List, List)}.
         *
         * <p>
         * This method requires hardware support. Hardware support can be determined using
         * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
         * {@link SoftApCapability#isFeatureSupported(int)}
         * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
         *
         * <p>
         * If the method is called on a device without hardware 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 enableClientControlByUser(boolean enabled) {
            mClientControlByUser = enabled;
            return this;
        }


        /**
         * This method together with {@link enableClientControlByUser(boolean)} control client
         * connections to the AP. If {@link enableClientControlByUser(false)} is configured than
         * this API has no effect and clients are allowed to associate to the AP (within limit of
         * max number of clients).
         *
         * If {@link enableClientControlByUser(true)} is configured then this API configures
         * 2 lists:
         * <ul>
         * <li>List of clients which are blocked. These are rejected.</li>
         * <li>List of clients which are explicitly allowed. These are auto-accepted.</li>
         * </ul>
         *
         * <p>
         * 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 blockedClientList list of clients which are not allowed to associate to the AP.
         * @param allowedClientList list of clients which are allowed to associate to the AP
         *                          without user pre-approval.
         * @return Builder for chaining.
         */
        @NonNull
        public Builder setClientList(@NonNull List<MacAddress> blockedClientList,
                @NonNull List<MacAddress> allowedClientList) {
            mBlockedClientList = new ArrayList<>(blockedClientList);
            mAllowedClientList = new ArrayList<>(allowedClientList);
            Iterator<MacAddress> iterator = mAllowedClientList.iterator();
            while (iterator.hasNext()) {
                MacAddress client = iterator.next();
                int index = mBlockedClientList.indexOf(client);
                if (index != -1) {
                    throw new IllegalArgumentException("A MacAddress exist in both list");
                }
            }
            return this;
        }
    }
}
+74 −3
Original line number Diff line number Diff line
@@ -666,7 +666,8 @@ public class WifiManager {
    public @interface SapStartFailure {}

    /**
     *  All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL}.
     *  All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL} and
     *  {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
     *
     *  @hide
     */
@@ -691,6 +692,37 @@ public class WifiManager {
    @SystemApi
    public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2;


    /** @hide */
    @IntDef(flag = false, prefix = { "SAP_CLIENT_BLOCKED_REASON_" }, value = {
        SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER,
        SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SapClientBlockedReason {}

    /**
     *  If Soft Ap client is blocked, this reason code means that client doesn't exist in the
     *  specified configuration {@link SoftApConfiguration.Builder#setClientList(List, List)}
     *  and the {@link SoftApConfiguration.Builder#enableClientControlByUser(true)}
     *  is configured as well.
     *  @hide
     */
    @SystemApi
    public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0;

    /**
     *  If Soft Ap client is blocked, this reason code means that no more clients can be
     *  associated to this AP since it reached maximum capacity. The maximum capacity is
     *  the minimum of {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} and
     *  {@link SoftApCapability#getMaxSupportedClients} which get from
     *  {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)}.
     *
     *  @hide
     */
    @SystemApi
    public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"IFACE_IP_MODE_"}, value = {
@@ -3229,8 +3261,17 @@ public class WifiManager {
    /**
     * Sets the Wi-Fi AP Configuration.
     *
     * If the API is called while the soft AP is enabled, the configuration will apply to
     * the current soft AP if the new configuration only includes
     * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)}
     * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)}
     * or {@link SoftApConfiguration.Builder#enableClientControlByUser(boolean)}
     * or {@link SoftApConfiguration.Builder#setClientList(List, List)}.
     *
     * Otherwise, the configuration changes will be applied when the Soft AP is next started
     * (the framework will not stop/start the AP).
     *
     * @param softApConfig  A valid SoftApConfiguration specifying the configuration of the SAP.

     * @return {@code true} if the operation succeeded, {@code false} otherwise
     *
     * @hide
@@ -3460,7 +3501,8 @@ public class WifiManager {
         *                      {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
         * @param failureReason reason when in failed state. One of
         *                      {@link #SAP_START_FAILURE_GENERAL},
         *                      {@link #SAP_START_FAILURE_NO_CHANNEL}
         *                      {@link #SAP_START_FAILURE_NO_CHANNEL},
         *                      {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}
         */
        default void onStateChanged(@WifiApState int state, @SapStartFailure int failureReason) {}

@@ -3489,6 +3531,22 @@ public class WifiManager {
            // Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported
            // client number) to the UI.
        }

        /**
         * Called when client trying to connect but device blocked the client with specific reason.
         *
         * Can be used to ask user to update client to allowed list or blocked list
         * when reason is {@link SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER}, or
         * indicate the block due to maximum supported client number limitation when reason is
         * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}.
         *
         * @param client the currently blocked client.
         * @param blockedReason one of blocked reason from {@link SapClientBlockedReason}
         */
        default void onBlockedClientConnecting(@NonNull WifiClient client,
                @SapClientBlockedReason int blockedReason) {
            // Do nothing: can be used to ask user to update client to allowed list or blocked list.
        }
    }

    /**
@@ -3555,6 +3613,19 @@ public class WifiManager {
                mCallback.onCapabilityChanged(capability);
            });
        }

        @Override
        public void onBlockedClientConnecting(@NonNull WifiClient client, int blockedReason) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "SoftApCallbackProxy: onBlockedClientConnecting: client=" + client
                        + " with reason = " + blockedReason);
            }

            Binder.clearCallingIdentity();
            mExecutor.execute(() -> {
                mCallback.onBlockedClientConnecting(client, blockedReason);
            });
        }
    }

    /**
+24 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import androidx.test.filters.SmallTest;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@SmallTest
@@ -112,12 +114,18 @@ public class SoftApConfigurationTest {

    @Test
    public void testWpa2WithAllFieldCustomized() {
        List<MacAddress> testBlockedClientList = new ArrayList<>();
        List<MacAddress> testAllowedClientList = new ArrayList<>();
        testBlockedClientList.add(MacAddress.fromString("11:22:33:44:55:66"));
        testAllowedClientList.add(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
        SoftApConfiguration original = new SoftApConfiguration.Builder()
                .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                .setHiddenSsid(true)
                .setMaxNumberOfClients(10)
                .setShutdownTimeoutMillis(500000)
                .enableClientControlByUser(true)
                .setClientList(testBlockedClientList, testAllowedClientList)
                .build();
        assertThat(original.getPassphrase()).isEqualTo("secretsecret");
        assertThat(original.getSecurityType()).isEqualTo(
@@ -127,6 +135,9 @@ public class SoftApConfigurationTest {
        assertThat(original.isHiddenSsid()).isEqualTo(true);
        assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
        assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
        assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
        assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
        assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList);

        SoftApConfiguration unparceled = parcelUnparcel(original);
        assertThat(unparceled).isNotSameAs(original);
@@ -238,4 +249,17 @@ public class SoftApConfigurationTest {
                .setShutdownTimeoutMillis(-1)
                .build();
    }

    @Test(expected = IllegalArgumentException.class)
    public void testsetClientListExceptionWhenExistMacAddressInBothList() {
        final MacAddress testMacAddress_1 = MacAddress.fromString("22:33:44:55:66:77");
        final MacAddress testMacAddress_2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
        ArrayList<MacAddress> testAllowedClientList = new ArrayList<>();
        testAllowedClientList.add(testMacAddress_1);
        testAllowedClientList.add(testMacAddress_2);
        ArrayList<MacAddress> testBlockedClientList = new ArrayList<>();
        testBlockedClientList.add(testMacAddress_1);
        SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
        configBuilder.setClientList(testBlockedClientList, testAllowedClientList);
    }
}
Loading