wifi: Add SoftAp Max Client control support

1. SoftAp Max Client parameter in SoftApConfiguration
2. Add new field in CarrierConfigManager to get carrier max client
setting.
3. Add new Callback: onCapabilityChanged to notify Settings to know
current supported maximum client number from carrier req and chip limit.

Bug: 142752869
Test: atest frameworks/base/wifi/tests/
Change-Id: I03366e22055233b9f77e0c1808e54a473569c9c7
This commit is contained in:
lesl
2019-12-12 17:16:30 +08:00
parent 4a0eafa3d1
commit e14a0909ef
10 changed files with 379 additions and 13 deletions

View File

@@ -5608,11 +5608,19 @@ package android.net.wifi {
field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd
}
public final class SoftApCapability implements android.os.Parcelable {
method public int describeContents();
method public int getMaxSupportedClients();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
}
public final class SoftApConfiguration implements android.os.Parcelable {
method public int describeContents();
method public int getBand();
method @Nullable public android.net.MacAddress getBssid();
method public int getChannel();
method public int getMaxNumberOfClients();
method public int getSecurityType();
method @Nullable public String getSsid();
method @Nullable public String getWpa2Passphrase();
@@ -5635,6 +5643,7 @@ package android.net.wifi {
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 setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
@@ -5868,6 +5877,7 @@ package android.net.wifi {
field public static final int PASSPOINT_ROAMING_NETWORK = 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
field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb
field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa
@@ -5906,6 +5916,7 @@ package android.net.wifi {
}
public static interface WifiManager.SoftApCallback {
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);
method public default void onStateChanged(int, int);
@@ -8908,6 +8919,11 @@ package android.telephony {
field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
public static final class CarrierConfigManager.Wifi {
field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
field public static final String KEY_PREFIX = "wifi.";
}
public final class CarrierRestrictionRules implements android.os.Parcelable {
method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
method public int describeContents();

View File

@@ -3932,6 +3932,32 @@ public class CarrierConfigManager {
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
}
/**
* Wi-Fi configs used in WiFi Module.
*
* @hide
*/
@SystemApi
public static final class Wifi {
/** Prefix of all Wifi.KEY_* constants. */
public static final String KEY_PREFIX = "wifi.";
/**
* It contains the maximum client count definition that the carrier owns.
*/
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
return defaults;
}
private Wifi() {}
}
/**

View File

@@ -15,6 +15,7 @@
*/
package android.net.wifi;
import android.net.wifi.SoftApCapability;
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiClient;
@@ -51,4 +52,12 @@ oneway interface ISoftApCallback
* @param softApInfo is the softap information. {@link SoftApInfo}
*/
void onInfoChanged(in SoftApInfo softApInfo);
/**
* Service to manager callback providing information of softap.
*
* @param capability is the softap capability. {@link SoftApCapability}
*/
void onCapabilityChanged(in SoftApCapability capability);
}

View File

@@ -0,0 +1,19 @@
/**
* 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;
parcelable SoftApCapability;

View File

@@ -0,0 +1,114 @@
/*
* 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.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* A class representing capability of the SoftAp.
* {@see WifiManager}
*
* @hide
*/
@SystemApi
public final class SoftApCapability implements Parcelable {
private int mMaximumSupportedClientNumber;
/**
* Get the maximum supported client numbers which AP resides on.
*/
public int getMaxSupportedClients() {
return mMaximumSupportedClientNumber;
}
/**
* Set the maximum supported client numbers which AP resides on.
* @hide
*/
public void setMaxSupportedClients(int maxClient) {
mMaximumSupportedClientNumber = maxClient;
}
/**
* @hide
*/
public SoftApCapability(@Nullable SoftApCapability source) {
if (source != null) {
mMaximumSupportedClientNumber = source.mMaximumSupportedClientNumber;
}
}
/**
* @hide
*/
public SoftApCapability() {
}
@Override
/** Implement the Parcelable interface. */
public int describeContents() {
return 0;
}
@Override
/** Implement the Parcelable interface */
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mMaximumSupportedClientNumber);
}
@NonNull
/** Implement the Parcelable interface */
public static final Creator<SoftApCapability> CREATOR = new Creator<SoftApCapability>() {
public SoftApCapability createFromParcel(Parcel in) {
SoftApCapability capability = new SoftApCapability();
capability.mMaximumSupportedClientNumber = in.readInt();
return capability;
}
public SoftApCapability[] newArray(int size) {
return new SoftApCapability[size];
}
};
@NonNull
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
sbuf.append("MaximumSupportedClientNumber=").append(mMaximumSupportedClientNumber);
return sbuf.toString();
}
@Override
public boolean equals(@NonNull Object o) {
if (this == o) return true;
if (!(o instanceof SoftApCapability)) return false;
SoftApCapability capability = (SoftApCapability) o;
return mMaximumSupportedClientNumber == capability.mMaximumSupportedClientNumber;
}
@Override
public int hashCode() {
return Objects.hash(mMaximumSupportedClientNumber);
}
}

View File

@@ -163,6 +163,11 @@ public final class SoftApConfiguration implements Parcelable {
*/
private final int mChannel;
/**
* 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 security types from {@link @SecurityType}
@@ -191,7 +196,7 @@ public final class SoftApConfiguration implements Parcelable {
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
@SecurityType int securityType) {
@SecurityType int securityType, int maxNumberOfClients) {
mSsid = ssid;
mBssid = bssid;
mWpa2Passphrase = wpa2Passphrase;
@@ -199,6 +204,7 @@ public final class SoftApConfiguration implements Parcelable {
mBand = band;
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
}
@Override
@@ -216,13 +222,14 @@ public final class SoftApConfiguration implements Parcelable {
&& mHiddenSsid == other.mHiddenSsid
&& mBand == other.mBand
&& mChannel == other.mChannel
&& mSecurityType == other.mSecurityType;
&& mSecurityType == other.mSecurityType
&& mMaxNumberOfClients == other.mMaxNumberOfClients;
}
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
mBand, mChannel, mSecurityType);
mBand, mChannel, mSecurityType, mMaxNumberOfClients);
}
@Override
@@ -236,6 +243,7 @@ public final class SoftApConfiguration implements Parcelable {
sbuf.append(" \n Band =").append(mBand);
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
return sbuf.toString();
}
@@ -248,6 +256,7 @@ public final class SoftApConfiguration implements Parcelable {
dest.writeInt(mBand);
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
}
@Override
@@ -262,7 +271,8 @@ public final class SoftApConfiguration implements Parcelable {
return new SoftApConfiguration(
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt());
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
in.readInt());
}
@Override
@@ -282,7 +292,7 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Returns MAC address set to be BSSID for the AP.
* {@link #setBssid(MacAddress)}.
* {@link Builder#setBssid(MacAddress)}.
*/
@Nullable
public MacAddress getBssid() {
@@ -291,7 +301,7 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Returns String set to be passphrase for the WPA2-PSK AP.
* {@link #setWpa2Passphrase(String)}.
* {@link Builder#setWpa2Passphrase(String)}.
*/
@Nullable
public String getWpa2Passphrase() {
@@ -301,7 +311,7 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
* not (false: broadcasts its SSID) for the AP.
* {@link #setHiddenSsid(boolean)}.
* {@link Builder#setHiddenSsid(boolean)}.
*/
public boolean isHiddenSsid() {
return mHiddenSsid;
@@ -309,7 +319,7 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Returns {@link BandType} set to be the band for the AP.
* {@link #setBand(@BandType int)}.
* {@link Builder#setBand(@BandType int)}.
*/
public @BandType int getBand() {
return mBand;
@@ -317,7 +327,7 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Returns Integer set to be the channel for the AP.
* {@link #setChannel(int)}.
* {@link Builder#setChannel(int)}.
*/
public int getChannel() {
return mChannel;
@@ -332,6 +342,14 @@ public final class SoftApConfiguration implements Parcelable {
return mSecurityType;
}
/**
* Returns the maximum number of clients that can associate to the AP.
* {@link Builder#setMaxNumberOfClients(int)}.
*/
public int getMaxNumberOfClients() {
return mMaxNumberOfClients;
}
/**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
@@ -346,6 +364,7 @@ public final class SoftApConfiguration implements Parcelable {
private boolean mHiddenSsid;
private int mBand;
private int mChannel;
private int mMaxNumberOfClients;
private int setSecurityType() {
int securityType = SECURITY_TYPE_OPEN;
@@ -369,6 +388,7 @@ public final class SoftApConfiguration implements Parcelable {
mHiddenSsid = false;
mBand = BAND_2GHZ;
mChannel = 0;
mMaxNumberOfClients = 0;
}
/**
@@ -383,6 +403,7 @@ public final class SoftApConfiguration implements Parcelable {
mHiddenSsid = other.mHiddenSsid;
mBand = other.mBand;
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
}
/**
@@ -393,7 +414,7 @@ public final class SoftApConfiguration implements Parcelable {
@NonNull
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
mHiddenSsid, mBand, mChannel, setSecurityType());
mHiddenSsid, mBand, mChannel, setSecurityType(), mMaxNumberOfClients);
}
/**
@@ -523,5 +544,37 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = channel;
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 hardware support. If the method is used to set a
* non-zero {@code maxNumberOfClients} value then
* {@link WifiManager#startTetheredHotspot} will report error code
* {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
*
* @param maxNumberOfClients maximum client number of the AP.
* @return Builder for chaining.
*/
@NonNull
public Builder setMaxNumberOfClients(int maxNumberOfClients) {
if (maxNumberOfClients < 0) {
throw new IllegalArgumentException("maxNumberOfClients should be not negative");
}
mMaxNumberOfClients = maxNumberOfClients;
return this;
}
}
}

View File

@@ -549,6 +549,7 @@ public class WifiManager {
* currently support general and no_channel
* @see #SAP_START_FAILURE_GENERAL
* @see #SAP_START_FAILURE_NO_CHANNEL
* @see #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION
*
* @hide
*/
@@ -652,6 +653,7 @@ public class WifiManager {
@IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = {
SAP_START_FAILURE_GENERAL,
SAP_START_FAILURE_NO_CHANNEL,
SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SapStartFailure {}
@@ -673,6 +675,15 @@ public class WifiManager {
@SystemApi
public static final int SAP_START_FAILURE_NO_CHANNEL = 1;
/**
* If Wi-Fi AP start failed, this reason code means that the specified configuration
* is not supported by the current HAL version.
*
* @hide
*/
@SystemApi
public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"IFACE_IP_MODE_"}, value = {
@@ -3431,6 +3442,16 @@ public class WifiManager {
default void onInfoChanged(@NonNull SoftApInfo softApInfo) {
// Do nothing: can be updated to add SoftApInfo details (e.g. channel) to the UI.
}
/**
* Called when capability of softap changes.
*
* @param softApCapability is the softap capability. {@link SoftApCapability}
*/
default void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
// Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported
// client number) to the UI.
}
}
/**
@@ -3484,6 +3505,19 @@ public class WifiManager {
mCallback.onInfoChanged(softApInfo);
});
}
@Override
public void onCapabilityChanged(SoftApCapability capability) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "SoftApCallbackProxy: onCapabilityChanged: SoftApCapability="
+ capability);
}
Binder.clearCallingIdentity();
mExecutor.execute(() -> {
mCallback.onCapabilityChanged(capability);
});
}
}
/**

View File

@@ -0,0 +1,69 @@
/*
* 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.os.Parcel;
import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
* Unit tests for {@link android.net.wifi.SoftApCapability}.
*/
@SmallTest
public class SoftApCapabilityTest {
/**
* Verifies copy constructor.
*/
@Test
public void testCopyOperator() throws Exception {
SoftApCapability capability = new SoftApCapability();
capability.setMaxSupportedClients(10);
SoftApCapability copiedCapability = new SoftApCapability(capability);
assertEquals(capability, copiedCapability);
assertEquals(capability.hashCode(), copiedCapability.hashCode());
}
/**
* Verifies parcel serialization/deserialization.
*/
@Test
public void testParcelOperation() throws Exception {
SoftApCapability capability = new SoftApCapability();
capability.setMaxSupportedClients(10);
Parcel parcelW = Parcel.obtain();
capability.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
parcelW.recycle();
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
SoftApCapability fromParcel = SoftApCapability.CREATOR.createFromParcel(parcelR);
assertEquals(capability, fromParcel);
assertEquals(capability.hashCode(), fromParcel.hashCode());
}
}

View File

@@ -50,6 +50,7 @@ public class SoftApConfigurationTest {
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
assertThat(original.getChannel()).isEqualTo(0);
assertThat(original.isHiddenSsid()).isEqualTo(false);
assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -73,7 +74,7 @@ public class SoftApConfigurationTest {
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
assertThat(original.getChannel()).isEqualTo(0);
assertThat(original.isHiddenSsid()).isEqualTo(false);
assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -87,12 +88,13 @@ public class SoftApConfigurationTest {
}
@Test
public void testWpa2WithBandAndChannelAndHiddenNetwork() {
public void testWpa2WithAllFieldCustomized() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
.setWpa2Passphrase("secretsecret")
.setBand(SoftApConfiguration.BAND_ANY)
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
.build();
assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
@@ -100,7 +102,7 @@ public class SoftApConfigurationTest {
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);

View File

@@ -877,6 +877,25 @@ public class WifiManagerTest {
verify(mSoftApCallback).onInfoChanged(testSoftApInfo);
}
/*
* Verify client-provided callback is being called through callback proxy
*/
@Test
public void softApCallbackProxyCallsOnCapabilityChanged() throws Exception {
SoftApCapability testSoftApCapability = new SoftApCapability();
testSoftApCapability.setMaxSupportedClients(10);
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().onCapabilityChanged(testSoftApCapability);
mLooper.dispatchAll();
verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability);
}
/*
* Verify client-provided callback is being called through callback proxy on multiple events
*/
@@ -885,6 +904,8 @@ public class WifiManagerTest {
SoftApInfo testSoftApInfo = new SoftApInfo();
testSoftApInfo.setFrequency(TEST_AP_FREQUENCY);
testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH);
SoftApCapability testSoftApCapability = new SoftApCapability();
testSoftApCapability.setMaxSupportedClients(10);
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
@@ -896,12 +917,15 @@ public class WifiManagerTest {
callbackCaptor.getValue().onConnectedClientsChanged(testClients);
callbackCaptor.getValue().onInfoChanged(testSoftApInfo);
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
callbackCaptor.getValue().onCapabilityChanged(testSoftApCapability);
mLooper.dispatchAll();
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0);
verify(mSoftApCallback).onConnectedClientsChanged(testClients);
verify(mSoftApCallback).onInfoChanged(testSoftApInfo);
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability);
}
/*