summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Gabriel Biren <gbiren@google.com> 2022-12-21 17:10:48 +0000
committer Gabriel Biren <gbiren@google.com> 2023-01-13 18:37:13 +0000
commit6b6e255a5549a1a0afc8ae3d2810e54183950f42 (patch)
treefe95d1e69f4703337f8d227a11ce9d9efbbd964f
parentf9878357ea634f103f25c32ea5da9ed0cb09e035 (diff)
Add system APIs to add and remove QoS policies.
Both APIs are currently unimplemented. Bug: 263288200 Test: atest WifiManagerTest \ android.net.wifi.QosPolicyParamsTest Change-Id: If0326b0ef8af7e8b4bd9618a49df4f9946236867
-rw-r--r--framework/aidl-export/android/net/wifi/QosPolicyParams.aidl19
-rw-r--r--framework/api/system-current.txt43
-rw-r--r--framework/java/android/net/wifi/BaseWifiService.java10
-rw-r--r--framework/java/android/net/wifi/IWifiManager.aidl5
-rw-r--r--framework/java/android/net/wifi/QosPolicyParams.java525
-rw-r--r--framework/java/android/net/wifi/WifiManager.java54
-rw-r--r--framework/tests/src/android/net/wifi/QosPolicyParamsTest.java169
-rw-r--r--framework/tests/src/android/net/wifi/WifiManagerTest.java34
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java33
9 files changed, 892 insertions, 0 deletions
diff --git a/framework/aidl-export/android/net/wifi/QosPolicyParams.aidl b/framework/aidl-export/android/net/wifi/QosPolicyParams.aidl
new file mode 100644
index 0000000000..090f86697c
--- /dev/null
+++ b/framework/aidl-export/android/net/wifi/QosPolicyParams.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2023, 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 QosPolicyParams;
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index c218b60199..8849fc892b 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -27,6 +27,47 @@ package android.net.wifi {
field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
}
+ public final class QosPolicyParams implements android.os.Parcelable {
+ method @Nullable public android.net.MacAddress getDestinationAddress();
+ method @Nullable public int[] getDestinationPortRange();
+ method public int getDirection();
+ method public int getDscp();
+ method public int getPolicyId();
+ method public int getProtocol();
+ method @Nullable public android.net.MacAddress getSourceAddress();
+ method public int getSourcePort();
+ method public int getUserPriority();
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.QosPolicyParams> CREATOR;
+ field public static final int DIRECTION_DOWNLINK = 1; // 0x1
+ field public static final int DIRECTION_UPLINK = 0; // 0x0
+ field public static final int DSCP_ANY = -1; // 0xffffffff
+ field public static final int PROTOCOL_ANY = -1; // 0xffffffff
+ field public static final int PROTOCOL_ESP = 50; // 0x32
+ field public static final int PROTOCOL_TCP = 6; // 0x6
+ field public static final int PROTOCOL_UDP = 17; // 0x11
+ field public static final int USER_PRIORITY_ANY = -1; // 0xffffffff
+ field public static final int USER_PRIORITY_BACKGROUND_HIGH = 2; // 0x2
+ field public static final int USER_PRIORITY_BACKGROUND_LOW = 1; // 0x1
+ field public static final int USER_PRIORITY_BEST_EFFORT_HIGH = 3; // 0x3
+ field public static final int USER_PRIORITY_BEST_EFFORT_LOW = 0; // 0x0
+ field public static final int USER_PRIORITY_VIDEO_HIGH = 5; // 0x5
+ field public static final int USER_PRIORITY_VIDEO_LOW = 4; // 0x4
+ field public static final int USER_PRIORITY_VOICE_HIGH = 7; // 0x7
+ field public static final int USER_PRIORITY_VOICE_LOW = 6; // 0x6
+ }
+
+ public static final class QosPolicyParams.Builder {
+ ctor public QosPolicyParams.Builder(int, int);
+ method @NonNull public android.net.wifi.QosPolicyParams build();
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setDestinationAddress(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setDestinationPortRange(int, int);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setDscp(int);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setProtocol(int);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setSourceAddress(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setSourcePort(int);
+ method @NonNull public android.net.wifi.QosPolicyParams.Builder setUserPriority(int);
+ }
+
@Deprecated public class RttManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
@@ -530,6 +571,7 @@ package android.net.wifi {
public class WifiManager {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void addQosPolicy(@NonNull android.net.wifi.QosPolicyParams);
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void addWifiVerboseLoggingStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
@@ -582,6 +624,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void removeAppState(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void removeQosPolicy(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeWifiVerboseLoggingStatusChangedListener(@NonNull android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener);
method @RequiresPermission(android.Manifest.permission.RESTART_WIFI_SUBSYSTEM) public void restartWifiSubsystem();
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
diff --git a/framework/java/android/net/wifi/BaseWifiService.java b/framework/java/android/net/wifi/BaseWifiService.java
index 960dfc8a9e..d3c5ba58f3 100644
--- a/framework/java/android/net/wifi/BaseWifiService.java
+++ b/framework/java/android/net/wifi/BaseWifiService.java
@@ -954,4 +954,14 @@ public class BaseWifiService extends IWifiManager.Stub {
public int getMaxNumberOfChannelsPerRequest() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void addQosPolicy(@NonNull QosPolicyParams policyParams, @NonNull IBinder binder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeQosPolicy(int policyId) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/framework/java/android/net/wifi/IWifiManager.aidl b/framework/java/android/net/wifi/IWifiManager.aidl
index 58ddbd8633..5b7ada7d56 100644
--- a/framework/java/android/net/wifi/IWifiManager.aidl
+++ b/framework/java/android/net/wifi/IWifiManager.aidl
@@ -47,6 +47,7 @@ import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.IWifiConnectedNetworkScorer;
import android.net.wifi.IWifiNetworkSelectionConfigListener;
import android.net.wifi.IWifiVerboseLoggingStatusChangedListener;
+import android.net.wifi.QosPolicyParams;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiAvailableChannel;
@@ -418,4 +419,8 @@ interface IWifiManager
void reportCreateInterfaceImpact(String packageName, int interfaceType, boolean requireNewInterface, in IInterfaceCreationInfoCallback callback);
int getMaxNumberOfChannelsPerRequest();
+
+ void addQosPolicy(in QosPolicyParams policyParams, in IBinder binder);
+
+ void removeQosPolicy(int policyId);
}
diff --git a/framework/java/android/net/wifi/QosPolicyParams.java b/framework/java/android/net/wifi/QosPolicyParams.java
new file mode 100644
index 0000000000..e3ddf1022d
--- /dev/null
+++ b/framework/java/android/net/wifi/QosPolicyParams.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2023 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.DscpPolicy;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Parameters for QoS policies requested by system applications.
+ * @hide
+ */
+@SystemApi
+public final class QosPolicyParams implements Parcelable {
+ private static final String TAG = "QosPolicyParams";
+
+ /**
+ * Indicates that the policy does not specify a DSCP value.
+ */
+ public static final int DSCP_ANY = -1;
+
+ /**
+ * Indicates that the policy does not specify a protocol.
+ */
+ public static final int PROTOCOL_ANY = DscpPolicy.PROTOCOL_ANY;
+
+ /**
+ * Policy should match packets using the TCP protocol.
+ */
+ public static final int PROTOCOL_TCP = 6;
+
+ /**
+ * Policy should match packets using the UDP protocol.
+ */
+ public static final int PROTOCOL_UDP = 17;
+
+ /**
+ * Policy should match packets using the ESP protocol.
+ */
+ public static final int PROTOCOL_ESP = 50;
+
+ /** @hide */
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ PROTOCOL_ANY,
+ PROTOCOL_TCP,
+ PROTOCOL_UDP,
+ PROTOCOL_ESP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protocol {}
+
+ /**
+ * Policy should match packets in the uplink direction.
+ */
+ public static final int DIRECTION_UPLINK = 0;
+
+ /**
+ * Policy should match packets in the downlink direction.
+ */
+ public static final int DIRECTION_DOWNLINK = 1;
+
+
+ /** @hide */
+ @IntDef(prefix = { "DIRECTION_" }, value = {
+ DIRECTION_UPLINK,
+ DIRECTION_DOWNLINK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Direction {}
+
+ /**
+ * Indicates that the policy does not specify a User Priority.
+ */
+ public static final int USER_PRIORITY_ANY = -1;
+
+ /**
+ * Policy should be assigned a low background priority.
+ */
+ public static final int USER_PRIORITY_BACKGROUND_LOW = 1;
+
+ /**
+ * Policy should be assigned a high background priority.
+ */
+ public static final int USER_PRIORITY_BACKGROUND_HIGH = 2;
+
+ /**
+ * Policy should be assigned a low best-effort priority.
+ */
+ public static final int USER_PRIORITY_BEST_EFFORT_LOW = 0;
+
+ /**
+ * Policy should be assigned a high best-effort priority.
+ */
+ public static final int USER_PRIORITY_BEST_EFFORT_HIGH = 3;
+
+ /**
+ * Policy should be assigned a low video priority.
+ */
+ public static final int USER_PRIORITY_VIDEO_LOW = 4;
+
+ /**
+ * Policy should be assigned a high video priority.
+ */
+ public static final int USER_PRIORITY_VIDEO_HIGH = 5;
+
+ /**
+ * Policy should be assigned a low voice priority.
+ */
+ public static final int USER_PRIORITY_VOICE_LOW = 6;
+
+ /**
+ * Policy should be assigned a high voice priority.
+ */
+ public static final int USER_PRIORITY_VOICE_HIGH = 7;
+
+ /** @hide */
+ @IntDef(prefix = { "USER_PRIORITY_" }, value = {
+ USER_PRIORITY_ANY,
+ USER_PRIORITY_BACKGROUND_LOW,
+ USER_PRIORITY_BACKGROUND_HIGH,
+ USER_PRIORITY_BEST_EFFORT_LOW,
+ USER_PRIORITY_BEST_EFFORT_HIGH,
+ USER_PRIORITY_VIDEO_LOW,
+ USER_PRIORITY_VIDEO_HIGH,
+ USER_PRIORITY_VOICE_LOW,
+ USER_PRIORITY_VOICE_HIGH,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserPriority {}
+
+ /**
+ * Unique policy ID. See {@link Builder#Builder(int, int)} for more information.
+ */
+ private final int mPolicyId;
+
+ // QoS DSCP marking. See {@link Builder#setDscp(int)} for more information.
+ private final int mDscp;
+
+ // User priority to apply to packets matching the policy. Only applicable to downlink requests.
+ private final int mUserPriority;
+
+ // Source address.
+ private final @Nullable MacAddress mSrcAddr;
+
+ // Destination address.
+ private final @Nullable MacAddress mDstAddr;
+
+ // Source port.
+ private final int mSrcPort;
+
+ // IP protocol that the policy requires.
+ private final @Protocol int mProtocol;
+
+ // Destination port range. Inclusive range.
+ private final @Nullable int[] mDstPortRange;
+
+ // Direction of traffic stream.
+ private final @Direction int mDirection;
+
+ private QosPolicyParams(int policyId, int dscp, @UserPriority int userPriority,
+ @Nullable MacAddress srcAddr, @Nullable MacAddress dstAddr, int srcPort,
+ @Protocol int protocol, @Nullable int[] dstPortRange, @Direction int direction) {
+ this.mPolicyId = policyId;
+ this.mDscp = dscp;
+ this.mUserPriority = userPriority;
+ this.mSrcAddr = srcAddr;
+ this.mDstAddr = dstAddr;
+ this.mSrcPort = srcPort;
+ this.mProtocol = protocol;
+ this.mDstPortRange = dstPortRange;
+ this.mDirection = direction;
+ }
+
+ /**
+ * Validate the parameters in this instance.
+ *
+ * @return true if all parameters are valid, false otherwise
+ * @hide
+ */
+ public boolean validate() {
+ if (mPolicyId < 1 || mPolicyId > 255) {
+ Log.e(TAG, "Policy ID not in valid range: " + mPolicyId);
+ return false;
+ }
+ if (mDscp < DSCP_ANY || mDscp > 63) {
+ Log.e(TAG, "DSCP value not in valid range: " + mDscp);
+ return false;
+ }
+ if (mUserPriority < USER_PRIORITY_ANY || mUserPriority > USER_PRIORITY_VOICE_HIGH) {
+ Log.e(TAG, "User priority not in valid range: " + mUserPriority);
+ return false;
+ }
+ if (mSrcPort < DscpPolicy.SOURCE_PORT_ANY || mSrcPort > 65535) {
+ Log.e(TAG, "Source port not in valid range: " + mSrcPort);
+ return false;
+ }
+ if (mDstPortRange != null && (mDstPortRange[0] < 0 || mDstPortRange[0] > 65535
+ || mDstPortRange[1] < 0 || mDstPortRange[1] > 65535)) {
+ Log.e(TAG, "Dst port range value not valid. start="
+ + mDstPortRange[0] + ", end=" + mDstPortRange[1]);
+ return false;
+ }
+ if (!(mDirection == DIRECTION_UPLINK || mDirection == DIRECTION_DOWNLINK)) {
+ Log.e(TAG, "Invalid direction enum: " + mDirection);
+ return false;
+ }
+
+ // Check DSCP and User Priority based on direction
+ if (mDirection == DIRECTION_UPLINK && mDscp == DSCP_ANY) {
+ Log.e(TAG, "DSCP must be provided for uplink requests");
+ return false;
+ }
+ if (mDirection == DIRECTION_DOWNLINK && mUserPriority == USER_PRIORITY_ANY) {
+ Log.e(TAG, "User priority must be provided for downlink requests");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the ID for this policy.
+ *
+ * See {@link Builder#Builder(int, int)} for more information.
+ */
+ public int getPolicyId() {
+ return mPolicyId;
+ }
+
+
+ /**
+ * Get the DSCP value for this policy.
+ *
+ * See {@link Builder#setDscp(int)} for more information.
+ *
+ * @return DSCP value, or {@link #DSCP_ANY} if not assigned.
+ */
+ public int getDscp() {
+ return mDscp;
+ }
+
+ /**
+ * Get the User Priority (UP) for this policy.
+ *
+ * See {@link Builder#setUserPriority(int)} for more information.
+ *
+ * @return User Priority value, or {@link #USER_PRIORITY_ANY} if not assigned.
+ */
+ public @UserPriority int getUserPriority() {
+ return mUserPriority;
+ }
+
+ /**
+ * Get the source address for this policy.
+ *
+ * See {@link Builder#setSourceAddress(MacAddress)} for more information.
+ *
+ * @return source address, or null if not assigned.
+ */
+ public @Nullable MacAddress getSourceAddress() {
+ return mSrcAddr;
+ }
+
+ /**
+ * Get the destination address for this policy.
+ *
+ * See {@link Builder#setDestinationAddress(MacAddress)} for more information.
+ *
+ * @return destination address, or null if not assigned.
+ */
+ public @Nullable MacAddress getDestinationAddress() {
+ return mDstAddr;
+ }
+
+ /**
+ * Get the source port for this policy.
+ *
+ * See {@link Builder#setSourcePort(int)} for more information.
+ *
+ * @return source port, or {@link DscpPolicy#SOURCE_PORT_ANY} if not assigned.
+ */
+ public int getSourcePort() {
+ return mSrcPort;
+ }
+
+ /**
+ * Get the protocol for this policy.
+ *
+ * See {@link Builder#setProtocol(int)} for more information.
+ *
+ * @return protocol, or {@link #PROTOCOL_ANY} if not assigned.
+ */
+ public @Protocol int getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * Get the destination port range for this policy.
+ *
+ * See {@link Builder#setDestinationPortRange(int, int)} for more information.
+ *
+ * @return destination port range, or null if not assigned.
+ */
+ public @Nullable int[] getDestinationPortRange() {
+ return mDstPortRange;
+ }
+
+ /**
+ * Get the direction for this policy.
+ *
+ * See {@link Builder#Builder(int, int)} for more information.
+ */
+ public @Direction int getDirection() {
+ return mDirection;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ QosPolicyParams that = (QosPolicyParams) o;
+ return mPolicyId == that.mPolicyId
+ && mDscp == that.mDscp
+ && mUserPriority == that.mUserPriority
+ && mSrcAddr.equals(that.mSrcAddr)
+ && mDstAddr.equals(that.mDstAddr)
+ && mSrcPort == that.mSrcPort
+ && mProtocol == that.mProtocol
+ && Arrays.equals(mDstPortRange, that.mDstPortRange)
+ && mDirection == that.mDirection;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPolicyId, mDscp, mUserPriority, mSrcAddr, mDstAddr, mSrcPort,
+ mProtocol, Arrays.hashCode(mDstPortRange), mDirection);
+ }
+
+ @Override
+ public String toString() {
+ return "{policyId=" + mPolicyId + ", "
+ + "dscp=" + mDscp + ", "
+ + "userPriority=" + mUserPriority + ", "
+ + "srcAddr=" + mSrcAddr + ", "
+ + "dstAddr=" + mDstAddr + ", "
+ + "srcPort=" + mSrcPort + ", "
+ + "protocol=" + mProtocol + ", "
+ + "dstPortRange=" + Arrays.toString(mDstPortRange) + ", "
+ + "direction=" + mDirection + "}";
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPolicyId);
+ dest.writeInt(mDscp);
+ dest.writeInt(mUserPriority);
+ dest.writeParcelable(mSrcAddr, 0);
+ dest.writeParcelable(mDstAddr, 0);
+ dest.writeInt(mSrcPort);
+ dest.writeInt(mProtocol);
+ dest.writeIntArray(mDstPortRange);
+ dest.writeInt(mDirection);
+ }
+
+ /** @hide */
+ QosPolicyParams(@NonNull Parcel in) {
+ this.mPolicyId = in.readInt();
+ this.mDscp = in.readInt();
+ this.mUserPriority = in.readInt();
+ this.mSrcAddr = in.readParcelable(MacAddress.class.getClassLoader());
+ this.mDstAddr = in.readParcelable(MacAddress.class.getClassLoader());
+ this.mSrcPort = in.readInt();
+ this.mProtocol = in.readInt();
+ this.mDstPortRange = in.createIntArray();
+ this.mDirection = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<QosPolicyParams> CREATOR =
+ new Parcelable.Creator<QosPolicyParams>() {
+ @Override
+ public QosPolicyParams createFromParcel(Parcel in) {
+ return new QosPolicyParams(in);
+ }
+
+ @Override
+ public QosPolicyParams[] newArray(int size) {
+ return new QosPolicyParams[size];
+ }
+ };
+
+ /**
+ * Builder for {@link QosPolicyParams}.
+ */
+ public static final class Builder {
+ private final int mPolicyId;
+ private final @Direction int mDirection;
+ private @Nullable MacAddress mSrcAddr;
+ private @Nullable MacAddress mDstAddr;
+ private int mDscp = DSCP_ANY;
+ private @UserPriority int mUserPriority = USER_PRIORITY_ANY;
+ private int mSrcPort = DscpPolicy.SOURCE_PORT_ANY;
+ private int mProtocol = PROTOCOL_ANY;
+ private @Nullable int[] mDstPortRange;
+
+ /**
+ * Constructor for {@link Builder}.
+ *
+ * @param policyId Unique ID to identify this policy. Each requesting application is
+ * responsible for maintaining policy IDs unique for that app. IDs must be
+ * in the range 1 <= policyId <= 255.
+ *
+ * In the case where a policy with an existing ID is created, the new policy
+ * will be rejected. To update an existing policy, remove the existing one
+ * before sending the new one.
+ * @param direction Whether this policy applies to the uplink or downlink direction.
+ */
+ public Builder(int policyId, @Direction int direction) {
+ mPolicyId = policyId;
+ mDirection = direction;
+ }
+
+ /**
+ * Specifies that this policy matches packets with the provided source address.
+ */
+ public @NonNull Builder setSourceAddress(@NonNull MacAddress value) {
+ Objects.requireNonNull(value, "Source address cannot be null");
+ mSrcAddr = value;
+ return this;
+ }
+
+ /**
+ * Specifies that this policy matches packets with the provided destination address.
+ */
+ public @NonNull Builder setDestinationAddress(@NonNull MacAddress value) {
+ Objects.requireNonNull(value, "Destination address cannot be null");
+ mDstAddr = value;
+ return this;
+ }
+
+ /**
+ * Specifies the DSCP value. For uplink requests, this value will be applied to packets
+ * that match the classifier. For downlink requests, this will be part of the classifier.
+ */
+ public @NonNull Builder setDscp(int value) {
+ mDscp = value;
+ return this;
+ }
+
+ /**
+ * Specifies that the provided User Priority should be applied to packets that
+ * match this classifier. Only applicable to downlink requests.
+ */
+ public @NonNull Builder setUserPriority(@UserPriority int value) {
+ mUserPriority = value;
+ return this;
+ }
+
+ /**
+ * Specifies that this policy matches packets with the provided source port.
+ */
+ public @NonNull Builder setSourcePort(int value) {
+ mSrcPort = value;
+ return this;
+ }
+
+ /**
+ * Specifies that this policy matches packets with the provided protocol.
+ */
+ public @NonNull Builder setProtocol(@Protocol int value) {
+ mProtocol = value;
+ return this;
+ }
+
+ /**
+ * Specifies that this policy matches packets with the provided destination port range.
+ */
+ public @NonNull Builder setDestinationPortRange(int start, int end) {
+ mDstPortRange = new int[]{start, end};
+ return this;
+ }
+
+ /**
+ * Construct a QosPolicyParams object with the specified parameters.
+ */
+ public @NonNull QosPolicyParams build() {
+ QosPolicyParams params = new QosPolicyParams(mPolicyId, mDscp, mUserPriority, mSrcAddr,
+ mDstAddr, mSrcPort, mProtocol, mDstPortRange, mDirection);
+ if (!params.validate()) {
+ throw new IllegalArgumentException("Provided parameters are invalid");
+ }
+ return params;
+ }
+ }
+}
diff --git a/framework/java/android/net/wifi/WifiManager.java b/framework/java/android/net/wifi/WifiManager.java
index 2aabf2b256..bae22629bb 100644
--- a/framework/java/android/net/wifi/WifiManager.java
+++ b/framework/java/android/net/wifi/WifiManager.java
@@ -10537,4 +10537,58 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Add a new application-initiated QoS policy.
+ *
+ * Note: Policies are managed using a policy ID, which can be retrieved using
+ * {@link QosPolicyParams#getPolicyId()}. This ID can be used when removing a policy via
+ * {@link #removeQosPolicy(int)}. The caller is in charge of assigning and managing the
+ * policy IDs for any requested policies.
+ *
+ * Note: Policies with duplicate IDs are not allowed. To update an existing policy, first
+ * remove it using {@link #removeQosPolicy(int)}, and then re-add it using this API.
+ *
+ * @param policyParams {@link QosPolicyParams} object describing the requested policy.
+ * @throws {@link SecurityException} if caller does not have the required permissions.
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ MANAGE_WIFI_NETWORK_SELECTION
+ })
+ public void addQosPolicy(@NonNull QosPolicyParams policyParams) {
+ try {
+ mService.addQosPolicy(policyParams, new Binder());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove an existing application-initiated QoS policy, previously added via
+ * {@link #addQosPolicy(QosPolicyParams)}.
+ *
+ * Note: The policy is identified by its policy ID, which is assigned by the caller. The ID
+ * for a given policy can be retrieved using {@link QosPolicyParams#getPolicyId()}.
+ *
+ * @param policyId ID of the policy to remove.
+ * @throws {@link SecurityException} if caller does not have the required permissions.
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ MANAGE_WIFI_NETWORK_SELECTION
+ })
+ public void removeQosPolicy(int policyId) {
+ try {
+ mService.removeQosPolicy(policyId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/framework/tests/src/android/net/wifi/QosPolicyParamsTest.java b/framework/tests/src/android/net/wifi/QosPolicyParamsTest.java
new file mode 100644
index 0000000000..15e61ae7e3
--- /dev/null
+++ b/framework/tests/src/android/net/wifi/QosPolicyParamsTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 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 org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.net.DscpPolicy;
+import android.net.MacAddress;
+import android.os.Parcel;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link QosPolicyParams}.
+ */
+public class QosPolicyParamsTest {
+ private static final int TEST_POLICY_ID = 127;
+ private static final int TEST_DIRECTION = QosPolicyParams.DIRECTION_DOWNLINK;
+ private static final int TEST_DSCP = 7;
+ private static final int TEST_USER_PRIORITY = QosPolicyParams.USER_PRIORITY_VIDEO_LOW;
+ private static final int TEST_SOURCE_PORT = 15;
+ private static final int TEST_PROTOCOL = QosPolicyParams.PROTOCOL_TCP;
+ private static final int[] TEST_DESTINATION_PORT_RANGE = new int[]{17, 20};
+ private static final MacAddress TEST_SOURCE_ADDRESS =
+ MacAddress.fromString("aa:bb:cc:dd:ee:ff");
+ private static final MacAddress TEST_DESTINATION_ADDRESS =
+ MacAddress.fromString("00:11:22:33:44:55");
+
+ @Before
+ public void setUp() {
+ assumeTrue(SdkLevel.isAtLeastU());
+ }
+
+ /**
+ * Creates a QosPolicyParams object will all fields assigned to a default test value.
+ */
+ private QosPolicyParams createTestQosPolicyParams() {
+ return new QosPolicyParams.Builder(TEST_POLICY_ID, TEST_DIRECTION)
+ .setUserPriority(TEST_USER_PRIORITY)
+ .setDscp(TEST_DSCP)
+ .setSourcePort(TEST_SOURCE_PORT)
+ .setProtocol(TEST_PROTOCOL)
+ .setDestinationPortRange(
+ TEST_DESTINATION_PORT_RANGE[0], TEST_DESTINATION_PORT_RANGE[1])
+ .setSourceAddress(TEST_SOURCE_ADDRESS)
+ .setDestinationAddress(TEST_DESTINATION_ADDRESS)
+ .build();
+ }
+
+ /**
+ * Check that all fields in the provided QosPolicyParams object match the default test values.
+ */
+ private void verifyTestQosPolicyParams(QosPolicyParams params) {
+ assertEquals(TEST_POLICY_ID, params.getPolicyId());
+ assertEquals(TEST_DIRECTION, params.getDirection());
+ assertEquals(TEST_USER_PRIORITY, params.getUserPriority());
+ assertEquals(TEST_DSCP, params.getDscp());
+ assertEquals(TEST_SOURCE_PORT, params.getSourcePort());
+ assertEquals(TEST_PROTOCOL, params.getProtocol());
+ assertArrayEquals(TEST_DESTINATION_PORT_RANGE, params.getDestinationPortRange());
+ assertTrue(TEST_SOURCE_ADDRESS.equals(params.getSourceAddress()));
+ assertTrue(TEST_DESTINATION_ADDRESS.equals(params.getDestinationAddress()));
+ }
+
+ /**
+ * Tests that the default parameters are set if they are not assigned by the user.
+ */
+ @Test
+ public void testDefaultParamsSet() {
+ QosPolicyParams params =
+ new QosPolicyParams.Builder(TEST_POLICY_ID, QosPolicyParams.DIRECTION_DOWNLINK)
+ .setUserPriority(TEST_USER_PRIORITY)
+ .build();
+ assertEquals(QosPolicyParams.DSCP_ANY, params.getDscp());
+ assertEquals(QosPolicyParams.PROTOCOL_ANY, params.getProtocol());
+ assertEquals(DscpPolicy.SOURCE_PORT_ANY, params.getSourcePort());
+ }
+
+ /**
+ * Test that if we set all the parameters in the Builder, the resulting QosPolicyParams
+ * object contains the expected values.
+ */
+ @Test
+ public void testSetAllParams() {
+ QosPolicyParams params = createTestQosPolicyParams();
+ verifyTestQosPolicyParams(params);
+ }
+
+ /**
+ * Tests that the Builder throws an exception if an invalid parameter is set.
+ */
+ @Test
+ public void testBuilderWithInvalidParam() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new QosPolicyParams.Builder(TEST_POLICY_ID, QosPolicyParams.DIRECTION_DOWNLINK)
+ .setUserPriority(TEST_USER_PRIORITY)
+ .setDscp(120) // DSCP should be <= 63
+ .build());
+ }
+
+ /**
+ * Tests that the Builder throws an exception if a null Mac Address is set.
+ */
+ @Test
+ public void testBuilderWithNullMacAddress() {
+ assertThrows(NullPointerException.class, () ->
+ new QosPolicyParams.Builder(TEST_POLICY_ID, QosPolicyParams.DIRECTION_DOWNLINK)
+ .setUserPriority(TEST_USER_PRIORITY)
+ .setSourceAddress(null)
+ .build());
+ }
+
+ /**
+ * Tests that the Builder throws an exception if a direction-specific error is found.
+ */
+ @Test
+ public void testBuilderWithDirectionSpecificError() {
+ assertThrows(IllegalArgumentException.class, () ->
+ // Policies for downlink are required to have a User Priority.
+ new QosPolicyParams.Builder(TEST_POLICY_ID, QosPolicyParams.DIRECTION_DOWNLINK)
+ .build());
+ }
+
+ /**
+ * Tests that the parceling logic can properly read and write from a Parcel.
+ */
+ @Test
+ public void testParcelReadWrite() {
+ QosPolicyParams params = createTestQosPolicyParams();
+ Parcel parcel = Parcel.obtain();
+ params.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ QosPolicyParams unparceledParams = QosPolicyParams.CREATOR.createFromParcel(parcel);
+ verifyTestQosPolicyParams(unparceledParams);
+ }
+
+ /**
+ * Tests that the overridden equality and hashCode operators properly compare two objects.
+ */
+ @Test
+ public void testObjectComparison() {
+ QosPolicyParams params1 = createTestQosPolicyParams();
+ QosPolicyParams params2 = createTestQosPolicyParams();
+ assertTrue(params1.equals(params2));
+ assertEquals(params1.hashCode(), params2.hashCode());
+ }
+}
diff --git a/framework/tests/src/android/net/wifi/WifiManagerTest.java b/framework/tests/src/android/net/wifi/WifiManagerTest.java
index 1a010c1caa..285d68cfe1 100644
--- a/framework/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/framework/tests/src/android/net/wifi/WifiManagerTest.java
@@ -3943,4 +3943,38 @@ public class WifiManagerTest {
verify(mWifiService).getChannelData(any(IListListener.Stub.class), eq(TEST_PACKAGE_NAME),
any(Bundle.class));
}
+
+ /**
+ * Verify call to {@link WifiManager#addQosPolicy(QosPolicyParams)}.
+ */
+ @Test
+ public void testAddQosPolicy() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastU());
+
+ final int policyId = 2;
+ final int direction = QosPolicyParams.DIRECTION_DOWNLINK;
+ final int userPriority = QosPolicyParams.USER_PRIORITY_VIDEO_LOW;
+ QosPolicyParams policyParams = new QosPolicyParams.Builder(policyId, direction)
+ .setUserPriority(userPriority)
+ .build();
+ ArgumentCaptor<QosPolicyParams> paramsCaptor =
+ ArgumentCaptor.forClass(QosPolicyParams.class);
+
+ mWifiManager.addQosPolicy(policyParams);
+ verify(mWifiService).addQosPolicy(paramsCaptor.capture(), any());
+ assertEquals(policyId, paramsCaptor.getValue().getPolicyId());
+ assertEquals(direction, paramsCaptor.getValue().getDirection());
+ assertEquals(userPriority, paramsCaptor.getValue().getUserPriority());
+ }
+
+ /**
+ * Verify call to {@link WifiManager#removeQosPolicy(int)}
+ */
+ @Test
+ public void testRemoveQosPolicy() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastU());
+ final int policyId = 127;
+ mWifiManager.removeQosPolicy(policyId);
+ verify(mWifiService).removeQosPolicy(eq(policyId));
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 0c15dc9e62..46559834aa 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -113,6 +113,7 @@ import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.IWifiConnectedNetworkScorer;
import android.net.wifi.IWifiNetworkSelectionConfigListener;
import android.net.wifi.IWifiVerboseLoggingStatusChangedListener;
+import android.net.wifi.QosPolicyParams;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApCapability;
import android.net.wifi.SoftApConfiguration;
@@ -7147,4 +7148,36 @@ public class WifiServiceImpl extends BaseWifiService {
return mContext.getResources()
.getInteger(R.integer.config_wifiNetworkSpecifierMaxPreferredChannels);
}
+
+ /**
+ * See {@link WifiManager#addQosPolicy(QosPolicyParams)}.
+ */
+ @Override
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void addQosPolicy(@NonNull QosPolicyParams policyParams, @NonNull IBinder binder) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new UnsupportedOperationException("SDK level too old");
+ }
+ int uid = Binder.getCallingUid();
+ if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
+ && !mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) {
+ throw new SecurityException("Uid=" + uid + " is not allowed to add QoS policies");
+ }
+ }
+
+ /**
+ * See {@link WifiManager#removeQosPolicy(int)}.
+ */
+ @Override
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void removeQosPolicy(int policyId) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new UnsupportedOperationException("SDK level too old");
+ }
+ int uid = Binder.getCallingUid();
+ if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
+ && !mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) {
+ throw new SecurityException("Uid=" + uid + " is not allowed to remove QoS policies");
+ }
+ }
}