Merge "Add APIs for Wifi/Cellular coex channel avoidance"

This commit is contained in:
Quang Luong
2020-12-01 22:18:03 +00:00
committed by Android (Google) Code Review
10 changed files with 746 additions and 1 deletions

View File

@@ -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";

View File

@@ -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 -->
<!-- ======================================= -->

View 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;

View File

@@ -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>);

View 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];
}
};
}

View 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();
}

View File

@@ -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);

View File

@@ -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);
}
}
}
/**

View 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);
}
}

View File

@@ -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.