Merge "Add APIs for Wifi/Cellular coex channel avoidance"
This commit is contained in:
@@ -248,7 +248,9 @@ package android {
|
||||
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
|
||||
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
|
||||
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
|
||||
field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
|
||||
field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
|
||||
field public static final String WIFI_UPDATE_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS";
|
||||
field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
|
||||
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
|
||||
field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
|
||||
|
||||
@@ -1815,6 +1815,16 @@
|
||||
<permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- @SystemApi @hide Allows system APK to update Wifi/Cellular coex channels to avoid.
|
||||
<p>Not for use by third-party applications. -->
|
||||
<permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided.
|
||||
<p>Not for use by third-party applications. -->
|
||||
<permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- ======================================= -->
|
||||
<!-- Permissions for short range, peripheral networks -->
|
||||
<!-- ======================================= -->
|
||||
|
||||
19
wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl
Normal file
19
wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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 CoexUnsafeChannel;
|
||||
@@ -1,6 +1,17 @@
|
||||
// Signature format: 2.0
|
||||
package android.net.wifi {
|
||||
|
||||
public final class CoexUnsafeChannel implements android.os.Parcelable {
|
||||
ctor public CoexUnsafeChannel(int, int);
|
||||
ctor public CoexUnsafeChannel(int, int, int);
|
||||
method public int getBand();
|
||||
method public int getChannel();
|
||||
method public int getPowerCapDbm();
|
||||
method public boolean isPowerCapAvailable();
|
||||
method public void setPowerCapDbm(int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.CoexUnsafeChannel> CREATOR;
|
||||
}
|
||||
|
||||
public abstract class EasyConnectStatusCallback {
|
||||
ctor public EasyConnectStatusCallback();
|
||||
method public void onBootstrapUriGenerated(@NonNull String);
|
||||
@@ -456,6 +467,8 @@ package android.net.wifi {
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset();
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
|
||||
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
|
||||
method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public int getCoexRestrictions();
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public java.util.Set<android.net.wifi.CoexUnsafeChannel> getCoexUnsafeChannels();
|
||||
method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode();
|
||||
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork();
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
|
||||
@@ -476,6 +489,7 @@ package android.net.wifi {
|
||||
method public boolean isVerboseLoggingEnabled();
|
||||
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
|
||||
method public boolean isWifiScannerSupported();
|
||||
method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void registerCoexCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.CoexCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
|
||||
@@ -487,6 +501,7 @@ package android.net.wifi {
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.Set<android.net.wifi.CoexUnsafeChannel>, int);
|
||||
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int);
|
||||
@@ -507,6 +522,7 @@ package android.net.wifi {
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopTemporarilyDisablingAllNonCarrierMergedWifi();
|
||||
method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void unregisterCoexCallback(@NonNull android.net.wifi.WifiManager.CoexCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
|
||||
@@ -520,6 +536,9 @@ package android.net.wifi {
|
||||
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
|
||||
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
|
||||
field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
|
||||
field public static final int COEX_RESTRICTION_SOFTAP = 2; // 0x2
|
||||
field public static final int COEX_RESTRICTION_WIFI_AWARE = 4; // 0x4
|
||||
field public static final int COEX_RESTRICTION_WIFI_DIRECT = 1; // 0x1
|
||||
field public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
|
||||
field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
|
||||
field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
|
||||
@@ -575,6 +594,11 @@ package android.net.wifi {
|
||||
method public void onSuccess();
|
||||
}
|
||||
|
||||
public abstract static class WifiManager.CoexCallback {
|
||||
ctor public WifiManager.CoexCallback();
|
||||
method public abstract void onCoexUnsafeChannelsChanged();
|
||||
}
|
||||
|
||||
public static interface WifiManager.NetworkRequestMatchCallback {
|
||||
method public default void onAbort();
|
||||
method public default void onMatch(@NonNull java.util.List<android.net.wifi.ScanResult>);
|
||||
|
||||
176
wifi/java/android/net/wifi/CoexUnsafeChannel.java
Normal file
176
wifi/java/android/net/wifi/CoexUnsafeChannel.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
|
||||
import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ;
|
||||
import static android.net.wifi.WifiScanner.WIFI_BAND_6_GHZ;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Data structure class representing a Wi-Fi channel that would cause interference to/receive
|
||||
* interference from the active cellular channels and should be avoided.
|
||||
*
|
||||
* If {@link #isPowerCapAvailable()} is {@code true}, then a valid power cap value is available
|
||||
* through {@link #getPowerCapDbm()} to be used if this channel cannot be avoided. If {@code false},
|
||||
* then {@link #getPowerCapDbm()} throws an IllegalStateException and the channel will not need to
|
||||
* cap its power.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class CoexUnsafeChannel implements Parcelable {
|
||||
private @WifiAnnotations.WifiBandBasic int mBand;
|
||||
private int mChannel;
|
||||
private boolean mIsPowerCapAvailable = false;
|
||||
private int mPowerCapDbm;
|
||||
|
||||
/**
|
||||
* Constructor for a CoexUnsafeChannel with no power cap specified.
|
||||
* @param band One of {@link WifiAnnotations.WifiBandBasic}
|
||||
* @param channel Channel number
|
||||
*/
|
||||
public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel) {
|
||||
mBand = band;
|
||||
mChannel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a CoexUnsafeChannel with power cap specified.
|
||||
* @param band One of {@link WifiAnnotations.WifiBandBasic}
|
||||
* @param channel Channel number
|
||||
* @param powerCapDbm Power cap in dBm
|
||||
*/
|
||||
public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel,
|
||||
int powerCapDbm) {
|
||||
mBand = band;
|
||||
mChannel = channel;
|
||||
setPowerCapDbm(powerCapDbm);
|
||||
}
|
||||
|
||||
/** Returns the Wi-Fi band of this channel as one of {@link WifiAnnotations.WifiBandBasic} */
|
||||
public @WifiAnnotations.WifiBandBasic int getBand() {
|
||||
return mBand;
|
||||
}
|
||||
|
||||
/** Returns the channel number of this channel. */
|
||||
public int getChannel() {
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
/** Returns {@code true} if {@link #getPowerCapDbm()} is a valid value, else {@code false} */
|
||||
public boolean isPowerCapAvailable() {
|
||||
return mIsPowerCapAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the power cap of this channel in dBm. Throws IllegalStateException if
|
||||
* {@link #isPowerCapAvailable()} is {@code false}.
|
||||
*/
|
||||
public int getPowerCapDbm() {
|
||||
if (!mIsPowerCapAvailable) {
|
||||
throw new IllegalStateException("getPowerCapDbm called but power cap is unavailable");
|
||||
}
|
||||
return mPowerCapDbm;
|
||||
}
|
||||
|
||||
/** Set the power cap of this channel. */
|
||||
public void setPowerCapDbm(int powerCapDbm) {
|
||||
mIsPowerCapAvailable = true;
|
||||
mPowerCapDbm = powerCapDbm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CoexUnsafeChannel that = (CoexUnsafeChannel) o;
|
||||
return mBand == that.mBand
|
||||
&& mChannel == that.mChannel
|
||||
&& mIsPowerCapAvailable == that.mIsPowerCapAvailable
|
||||
&& mPowerCapDbm == that.mPowerCapDbm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mBand, mChannel, mIsPowerCapAvailable, mPowerCapDbm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sj = new StringBuilder("CoexUnsafeChannel{");
|
||||
sj.append(mChannel);
|
||||
sj.append(", ");
|
||||
if (mBand == WIFI_BAND_24_GHZ) {
|
||||
sj.append("2.4GHz");
|
||||
} else if (mBand == WIFI_BAND_5_GHZ) {
|
||||
sj.append("5GHz");
|
||||
} else if (mBand == WIFI_BAND_6_GHZ) {
|
||||
sj.append("6GHz");
|
||||
} else {
|
||||
sj.append("UNKNOWN BAND");
|
||||
}
|
||||
if (mIsPowerCapAvailable) {
|
||||
sj.append(", ").append(mPowerCapDbm).append("dBm");
|
||||
}
|
||||
sj.append('}');
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface {@hide} */
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface {@hide} */
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mBand);
|
||||
dest.writeInt(mChannel);
|
||||
dest.writeBoolean(mIsPowerCapAvailable);
|
||||
if (mIsPowerCapAvailable) {
|
||||
dest.writeInt(mPowerCapDbm);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
public static final @NonNull Creator<CoexUnsafeChannel> CREATOR =
|
||||
new Creator<CoexUnsafeChannel>() {
|
||||
public CoexUnsafeChannel createFromParcel(Parcel in) {
|
||||
final int band = in.readInt();
|
||||
final int channel = in.readInt();
|
||||
final boolean isPowerCapAvailable = in.readBoolean();
|
||||
if (isPowerCapAvailable) {
|
||||
final int powerCapDbm = in.readInt();
|
||||
return new CoexUnsafeChannel(band, channel, powerCapDbm);
|
||||
}
|
||||
return new CoexUnsafeChannel(band, channel);
|
||||
}
|
||||
|
||||
public CoexUnsafeChannel[] newArray(int size) {
|
||||
return new CoexUnsafeChannel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
26
wifi/java/android/net/wifi/ICoexCallback.aidl
Normal file
26
wifi/java/android/net/wifi/ICoexCallback.aidl
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
/**
|
||||
* Interface for Wi-Fi/cellular coex callback.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ICoexCallback
|
||||
{
|
||||
void onCoexUnsafeChannelsChanged();
|
||||
}
|
||||
@@ -24,7 +24,9 @@ import android.net.wifi.hotspot2.IProvisioningCallback;
|
||||
|
||||
import android.net.DhcpInfo;
|
||||
import android.net.Network;
|
||||
import android.net.wifi.CoexUnsafeChannel;
|
||||
import android.net.wifi.IActionListener;
|
||||
import android.net.wifi.ICoexCallback;
|
||||
import android.net.wifi.IDppCallback;
|
||||
import android.net.wifi.ILocalOnlyHotspotCallback;
|
||||
import android.net.wifi.INetworkRequestMatchCallback;
|
||||
@@ -144,6 +146,16 @@ interface IWifiManager
|
||||
|
||||
void updateInterfaceIpState(String ifaceName, int mode);
|
||||
|
||||
void setCoexUnsafeChannels(in List<CoexUnsafeChannel> unsafeChannels, int mandatoryRestrictions);
|
||||
|
||||
List<CoexUnsafeChannel> getCoexUnsafeChannels();
|
||||
|
||||
int getCoexRestrictions();
|
||||
|
||||
void registerCoexCallback(in ICoexCallback callback);
|
||||
|
||||
void unregisterCoexCallback(in ICoexCallback callback);
|
||||
|
||||
boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName);
|
||||
|
||||
boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName);
|
||||
|
||||
@@ -72,6 +72,7 @@ import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -3110,6 +3111,238 @@ public class WifiManager {
|
||||
}
|
||||
}
|
||||
|
||||
/* Wi-Fi/Cellular Coex */
|
||||
|
||||
/**
|
||||
* Mandatory coex restriction flag for Wi-Fi Direct.
|
||||
*
|
||||
* @see #setCoexUnsafeChannels(Set, int)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int COEX_RESTRICTION_WIFI_DIRECT = 0x1 << 0;
|
||||
|
||||
/**
|
||||
* Mandatory coex restriction flag for SoftAP
|
||||
*
|
||||
* @see #setCoexUnsafeChannels(Set, int)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int COEX_RESTRICTION_SOFTAP = 0x1 << 1;
|
||||
|
||||
/**
|
||||
* Mandatory coex restriction flag for Wi-Fi Aware.
|
||||
*
|
||||
* @see #setCoexUnsafeChannels(Set, int)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int COEX_RESTRICTION_WIFI_AWARE = 0x1 << 2;
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, prefix = {"COEX_RESTRICTION_"}, value = {
|
||||
COEX_RESTRICTION_WIFI_DIRECT,
|
||||
COEX_RESTRICTION_SOFTAP,
|
||||
COEX_RESTRICTION_WIFI_AWARE
|
||||
})
|
||||
public @interface CoexRestriction {}
|
||||
|
||||
/**
|
||||
* Specify the set of {@link CoexUnsafeChannel} to propagate through the framework for
|
||||
* Wi-Fi/Cellular coex channel avoidance if the default algorithm is disabled via overlay
|
||||
* (i.e. config_wifiCoexDefaultAlgorithmEnabled = false). Otherwise do nothing.
|
||||
*
|
||||
* @param unsafeChannels Set of {@link CoexUnsafeChannel} to avoid.
|
||||
* @param restrictions Bitmap of {@link CoexRestriction} specifying the mandatory restricted
|
||||
* uses of the specified channels. If any restrictions are set, then the
|
||||
* supplied CoexUnsafeChannels will be completely avoided for the
|
||||
* specified modes, rather than be avoided with best effort.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS)
|
||||
public void setCoexUnsafeChannels(@NonNull Set<CoexUnsafeChannel> unsafeChannels,
|
||||
int restrictions) {
|
||||
if (unsafeChannels == null) {
|
||||
throw new IllegalArgumentException("unsafeChannels must not be null");
|
||||
}
|
||||
try {
|
||||
mService.setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of current {@link CoexUnsafeChannel} being used for Wi-Fi/Cellular coex
|
||||
* channel avoidance.
|
||||
*
|
||||
* This returns the set calculated by the default algorithm if
|
||||
* config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the set supplied
|
||||
* in {@link #setCoexUnsafeChannels(Set, int)}.
|
||||
*
|
||||
* If any {@link CoexRestriction} flags are set in {@link #getCoexRestrictions()}, then the
|
||||
* CoexUnsafeChannels should be totally avoided (i.e. not best effort) for the Wi-Fi modes
|
||||
* specified by the flags.
|
||||
*
|
||||
* @return Set of current CoexUnsafeChannels.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
|
||||
public Set<CoexUnsafeChannel> getCoexUnsafeChannels() {
|
||||
try {
|
||||
return new HashSet<>(mService.getCoexUnsafeChannels());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current coex restrictions being used for Wi-Fi/Cellular coex
|
||||
* channel avoidance.
|
||||
*
|
||||
* This returns the restrictions calculated by the default algorithm if
|
||||
* config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the value supplied
|
||||
* in {@link #setCoexUnsafeChannels(Set, int)}.
|
||||
*
|
||||
* @return int containing a bitwise-OR combination of {@link CoexRestriction}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
|
||||
public int getCoexRestrictions() {
|
||||
try {
|
||||
return mService.getCoexRestrictions();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a CoexCallback to listen on the current CoexUnsafeChannels and restrictions being
|
||||
* used for Wi-Fi/cellular coex channel avoidance.
|
||||
* @param executor Executor to execute listener callback on
|
||||
* @param callback CoexCallback to register
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
|
||||
public void registerCoexCallback(
|
||||
@NonNull @CallbackExecutor Executor executor, @NonNull CoexCallback callback) {
|
||||
if (executor == null) throw new IllegalArgumentException("executor must not be null");
|
||||
if (callback == null) throw new IllegalArgumentException("callback must not be null");
|
||||
CoexCallback.CoexCallbackProxy proxy = callback.getProxy();
|
||||
proxy.initProxy(executor, callback);
|
||||
try {
|
||||
mService.registerCoexCallback(proxy);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a CoexCallback from listening on the current CoexUnsafeChannels and restrictions
|
||||
* being used for Wi-Fi/cellular coex channel avoidance.
|
||||
* @param callback CoexCallback to unregister
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS)
|
||||
public void unregisterCoexCallback(@NonNull CoexCallback callback) {
|
||||
if (callback == null) throw new IllegalArgumentException("callback must not be null");
|
||||
CoexCallback.CoexCallbackProxy proxy = callback.getProxy();
|
||||
try {
|
||||
mService.unregisterCoexCallback(proxy);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
} finally {
|
||||
proxy.cleanUpProxy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract callback class for applications to receive updates about current CoexUnsafeChannels
|
||||
* for Wi-Fi/Cellular coex channel avoidance.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract static class CoexCallback {
|
||||
private final CoexCallbackProxy mCoexCallbackProxy;
|
||||
|
||||
public CoexCallback() {
|
||||
mCoexCallbackProxy = new CoexCallbackProxy();
|
||||
}
|
||||
|
||||
/*package*/ @NonNull
|
||||
CoexCallbackProxy getProxy() {
|
||||
return mCoexCallbackProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the current CoexUnsafeChannels or restrictions have changed.
|
||||
* Clients should call {@link #getCoexUnsafeChannels()} and {@link #getCoexRestrictions()}
|
||||
* to get the updated values.
|
||||
*/
|
||||
public abstract void onCoexUnsafeChannelsChanged();
|
||||
|
||||
/**
|
||||
* Callback proxy for CoexCallback objects.
|
||||
*/
|
||||
private static class CoexCallbackProxy extends ICoexCallback.Stub {
|
||||
private final Object mLock = new Object();
|
||||
@Nullable @GuardedBy("mLock") private Executor mExecutor;
|
||||
@Nullable @GuardedBy("mLock") private CoexCallback mCallback;
|
||||
|
||||
CoexCallbackProxy() {
|
||||
mExecutor = null;
|
||||
mCallback = null;
|
||||
}
|
||||
|
||||
/*package*/ void initProxy(@NonNull Executor executor,
|
||||
@NonNull CoexCallback callback) {
|
||||
synchronized (mLock) {
|
||||
mExecutor = executor;
|
||||
mCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void cleanUpProxy() {
|
||||
synchronized (mLock) {
|
||||
mExecutor = null;
|
||||
mCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoexUnsafeChannelsChanged() {
|
||||
Executor executor;
|
||||
CoexCallback callback;
|
||||
synchronized (mLock) {
|
||||
executor = mExecutor;
|
||||
callback = mCallback;
|
||||
}
|
||||
if (executor == null || callback == null) {
|
||||
return;
|
||||
}
|
||||
Binder.clearCallingIdentity();
|
||||
executor.execute(callback::onCoexUnsafeChannelsChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
|
||||
* Note that starting Soft AP mode may disable station mode operation if the device does not
|
||||
@@ -5991,7 +6224,6 @@ public class WifiManager {
|
||||
executor.execute(callback::onScanResultsAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
97
wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java
Normal file
97
wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link android.net.wifi.CoexUnsafeChannel}.
|
||||
*/
|
||||
@SmallTest
|
||||
public class CoexUnsafeChannelTest {
|
||||
/**
|
||||
* Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns false if no cap is set.
|
||||
*/
|
||||
@Test
|
||||
public void testIsPowerCapAvailable_noPowerCap_returnsFalse() {
|
||||
CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
|
||||
|
||||
assertThat(unsafeChannel.isPowerCapAvailable()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns true if a cap is set, and
|
||||
* {@link CoexUnsafeChannel#getPowerCapDbm()} returns the set value.
|
||||
*/
|
||||
@Test
|
||||
public void testIsPowerCapAvailable_powerCapSet_returnsTrue() {
|
||||
final int powerCapDbm = -50;
|
||||
CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
|
||||
|
||||
unsafeChannel.setPowerCapDbm(powerCapDbm);
|
||||
|
||||
assertThat(unsafeChannel.isPowerCapAvailable()).isTrue();
|
||||
assertThat(unsafeChannel.getPowerCapDbm()).isEqualTo(powerCapDbm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies {@link CoexUnsafeChannel#getPowerCapDbm()} throws an IllegalStateException if
|
||||
* {@link CoexUnsafeChannel#isPowerCapAvailable()} is {@code false}.
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetPowerCap_powerCapUnavailable_throwsException() {
|
||||
CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
|
||||
|
||||
unsafeChannel.getPowerCapDbm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify parcel read/write for CoexUnsafeChannel with or without power cap.
|
||||
*/
|
||||
@Test
|
||||
public void testParcelReadWrite_withOrWithoutCap_readEqualsWritten() throws Exception {
|
||||
CoexUnsafeChannel writeUnsafeChannelNoCap =
|
||||
new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6);
|
||||
CoexUnsafeChannel writeUnsafeChannelCapped =
|
||||
new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6, -50);
|
||||
|
||||
CoexUnsafeChannel readUnsafeChannelNoCap = parcelReadWrite(writeUnsafeChannelNoCap);
|
||||
CoexUnsafeChannel readUnsafeChannelCapped = parcelReadWrite(writeUnsafeChannelCapped);
|
||||
|
||||
assertThat(writeUnsafeChannelNoCap).isEqualTo(readUnsafeChannelNoCap);
|
||||
assertThat(writeUnsafeChannelCapped).isEqualTo(readUnsafeChannelCapped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the provided {@link CoexUnsafeChannel} to a parcel and deserialize it.
|
||||
*/
|
||||
private static CoexUnsafeChannel parcelReadWrite(CoexUnsafeChannel writeResult)
|
||||
throws Exception {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
writeResult.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
|
||||
return CoexUnsafeChannel.CREATOR.createFromParcel(parcel);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ package android.net.wifi;
|
||||
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
|
||||
import static android.net.wifi.WifiManager.ActionListener;
|
||||
import static android.net.wifi.WifiManager.BUSY;
|
||||
import static android.net.wifi.WifiManager.COEX_RESTRICTION_SOFTAP;
|
||||
import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_AWARE;
|
||||
import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_DIRECT;
|
||||
import static android.net.wifi.WifiManager.ERROR;
|
||||
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
|
||||
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
|
||||
@@ -43,6 +46,7 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER;
|
||||
import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
|
||||
import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
|
||||
import static android.net.wifi.WifiManager.WpsCallback;
|
||||
import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -74,6 +78,7 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.net.DhcpInfo;
|
||||
import android.net.MacAddress;
|
||||
import android.net.wifi.WifiManager.CoexCallback;
|
||||
import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
|
||||
import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
|
||||
import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
|
||||
@@ -108,9 +113,11 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -151,6 +158,7 @@ public class WifiManagerTest {
|
||||
private WifiManager mWifiManager;
|
||||
private WifiNetworkSuggestion mWifiNetworkSuggestion;
|
||||
private ScanResultsCallback mScanResultsCallback;
|
||||
private CoexCallback mCoexCallback;
|
||||
private WifiActivityEnergyInfo mWifiActivityEnergyInfo;
|
||||
|
||||
/**
|
||||
@@ -214,9 +222,148 @@ public class WifiManagerTest {
|
||||
mRunnable.run();
|
||||
}
|
||||
};
|
||||
mCoexCallback = new CoexCallback() {
|
||||
@Override
|
||||
public void onCoexUnsafeChannelsChanged() {
|
||||
mRunnable.run();
|
||||
}
|
||||
};
|
||||
mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the call to setCoexUnsafeChannels calls WifiServiceImpl to setCoexUnsafeChannels with
|
||||
* the provided CoexUnsafeChannels and restrictions bitmask.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception {
|
||||
Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>();
|
||||
int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP
|
||||
| COEX_RESTRICTION_WIFI_AWARE;
|
||||
|
||||
mWifiManager.setCoexUnsafeChannels(unsafeChannels, restrictions);
|
||||
|
||||
verify(mWifiService).setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an IllegalArgumentException if passed a null value for unsafeChannels.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCoexUnsafeChannelsThrowsIllegalArgumentExceptionOnNullUnsafeChannels() {
|
||||
try {
|
||||
mWifiManager.setCoexUnsafeChannels(null, 0);
|
||||
fail("expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the call to getCoexUnsafeChannels calls WifiServiceImpl to return the values from
|
||||
* getCoexUnsafeChannels.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception {
|
||||
Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>();
|
||||
unsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6));
|
||||
when(mWifiService.getCoexUnsafeChannels()).thenReturn(new ArrayList<>(unsafeChannels));
|
||||
|
||||
assertEquals(mWifiManager.getCoexUnsafeChannels(), unsafeChannels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify call to getCoexRestrictions calls WifiServiceImpl to return the value from
|
||||
* getCoexRestrictions.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCoexRestrictionsGoesToWifiServiceImpl() throws Exception {
|
||||
int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP
|
||||
| COEX_RESTRICTION_WIFI_AWARE;
|
||||
when(mWifiService.getCoexRestrictions()).thenReturn(restrictions);
|
||||
|
||||
assertEquals(mWifiService.getCoexRestrictions(), restrictions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify an IllegalArgumentException is thrown if callback is not provided.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testRegisterCoexCallbackWithNullCallback() throws Exception {
|
||||
mWifiManager.registerCoexCallback(mExecutor, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an IllegalArgumentException is thrown if executor is not provided.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testRegisterCoexCallbackWithNullExecutor() throws Exception {
|
||||
mWifiManager.registerCoexCallback(null, mCoexCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client provided callback is being called to the right callback.
|
||||
*/
|
||||
@Test
|
||||
public void testAddCoexCallbackAndReceiveEvent() throws Exception {
|
||||
ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
|
||||
ArgumentCaptor.forClass(ICoexCallback.Stub.class);
|
||||
mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
|
||||
verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
|
||||
callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client provided callback is being called to the right executor.
|
||||
*/
|
||||
@Test
|
||||
public void testRegisterCoexCallbackWithTheTargetExecutor() throws Exception {
|
||||
ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
|
||||
ArgumentCaptor.forClass(ICoexCallback.Stub.class);
|
||||
mWifiManager.registerCoexCallback(mExecutor, mCoexCallback);
|
||||
verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
|
||||
mWifiManager.registerCoexCallback(mAnotherExecutor, mCoexCallback);
|
||||
callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
|
||||
verify(mExecutor, never()).execute(any(Runnable.class));
|
||||
verify(mAnotherExecutor).execute(any(Runnable.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client register unregister then register again, to ensure callback still works.
|
||||
*/
|
||||
@Test
|
||||
public void testRegisterUnregisterThenRegisterAgainWithCoexCallback() throws Exception {
|
||||
ArgumentCaptor<ICoexCallback.Stub> callbackCaptor =
|
||||
ArgumentCaptor.forClass(ICoexCallback.Stub.class);
|
||||
mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
|
||||
verify(mWifiService).registerCoexCallback(callbackCaptor.capture());
|
||||
mWifiManager.unregisterCoexCallback(mCoexCallback);
|
||||
callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
|
||||
verify(mRunnable, never()).run();
|
||||
mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback);
|
||||
callbackCaptor.getValue().onCoexUnsafeChannelsChanged();
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client unregisterCoexCallback.
|
||||
*/
|
||||
@Test
|
||||
public void testUnregisterCoexCallback() throws Exception {
|
||||
mWifiManager.unregisterCoexCallback(mCoexCallback);
|
||||
verify(mWifiService).unregisterCoexCallback(any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client unregisterCoexCallback with null callback will cause an exception.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testUnregisterCoexCallbackWithNullCallback() throws Exception {
|
||||
mWifiManager.unregisterCoexCallback(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the call to startSoftAp calls WifiService to startSoftAp with the provided
|
||||
* WifiConfiguration. Verify that the return value is propagated to the caller.
|
||||
|
||||
Reference in New Issue
Block a user