diff options
| -rwxr-xr-x | api/system-current.txt | 8 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/ISoftApCallback.aidl | 10 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/SoftApConfiguration.java | 161 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/WifiManager.java | 77 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java | 24 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/WifiManagerTest.java | 20 |
6 files changed, 291 insertions, 9 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b30897ffd9e1..6da15248f01d 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5848,7 +5848,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(); @@ -5856,6 +5858,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 @@ -5873,9 +5876,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); @@ -6114,6 +6119,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 @@ -6155,6 +6162,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); diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl index 482b4910921d..f81bcb9e06d7 100644 --- a/wifi/java/android/net/wifi/ISoftApCallback.aidl +++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl @@ -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); } diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 2b7f8af728d7..a77d30a18817 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -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; @@ -182,6 +185,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 @@ -387,6 +423,34 @@ public final class SoftApConfiguration implements Parcelable { } /** + * 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; + } } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 1baab12b8ab5..729ac14b8b73 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -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); + }); + } } /** diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index eeea7e2a6cd8..6884a4ede27a 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -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); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 4b837184dc9a..5bdc34402cc4 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -70,6 +70,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.net.DhcpInfo; +import android.net.MacAddress; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; @@ -897,6 +898,25 @@ public class WifiManagerTest { } /* + * Verify client-provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnBlockedClientConnecting() throws Exception { + WifiClient testWifiClient = new WifiClient(MacAddress.fromString("22:33:44:55:66:77")); + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onBlockedClientConnecting(testWifiClient, + WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); + mLooper.dispatchAll(); + verify(mSoftApCallback).onBlockedClientConnecting(testWifiClient, + WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS); + } + + /* * Verify client-provided callback is being called through callback proxy on multiple events */ @Test |