blob: b46abf87e2387b7cdfd12f5622346bb5ee397d0d [file] [log] [blame]
/*
* 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.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.DscpPolicy;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
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 {}
/**
* Indicates that the policy does not specify an IP version.
*/
public static final int IP_VERSION_ANY = -1;
/**
* Policy should match packets using IPv4.
*/
public static final int IP_VERSION_4 = 4;
/**
* Policy should match packets using IPv6.
*/
public static final int IP_VERSION_6 = 6;
/** @hide */
@IntDef(prefix = { "IP_VERSION_" }, value = {
IP_VERSION_ANY,
IP_VERSION_4,
IP_VERSION_6
})
@Retention(RetentionPolicy.SOURCE)
public @interface IpVersion {}
/**
* Indicates that the policy does not specify a destination port.
*/
public static final int DESTINATION_PORT_ANY = -1;
/**
* Unique policy ID. See {@link Builder#Builder(int, int)} for more information.
*/
private final int mPolicyId;
/**
* Translated policy ID. Should only be set by the Wi-Fi service.
* @hide
*/
private int mTranslatedPolicyId;
// 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 IP address.
private final @Nullable InetAddress mSrcIp;
// Destination IP address.
private final @Nullable InetAddress mDstIp;
// Source port.
private final int mSrcPort;
// IP protocol that the policy requires.
private final @Protocol int mProtocol;
// Single destination port. Only applicable to downlink requests.
private final int mDstPort;
// Destination port range. Inclusive range. Only applicable to uplink requests.
private final @Nullable int[] mDstPortRange;
// Direction of traffic stream.
private final @Direction int mDirection;
// IP version. Only applicable to downlink requests.
private final @IpVersion int mIpVersion;
// Flow label. Only applicable to downlink requests using IPv6.
private final @Nullable byte[] mFlowLabel;
private QosPolicyParams(int policyId, int dscp, @UserPriority int userPriority,
@Nullable InetAddress srcIp, @Nullable InetAddress dstIp, int srcPort,
@Protocol int protocol, @Nullable int[] dstPortRange, @Direction int direction,
@IpVersion int ipVersion, int dstPort, @Nullable byte[] flowLabel) {
this.mPolicyId = policyId;
this.mDscp = dscp;
this.mUserPriority = userPriority;
this.mSrcIp = srcIp;
this.mDstIp = dstIp;
this.mSrcPort = srcPort;
this.mProtocol = protocol;
this.mDstPort = dstPort;
this.mDstPortRange = dstPortRange;
this.mDirection = direction;
this.mIpVersion = ipVersion;
this.mFlowLabel = flowLabel;
}
/**
* 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 (mDstPort < DESTINATION_PORT_ANY || mDstPort > 65535) {
Log.e(TAG, "Destination port not in valid range: " + mDstPort);
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;
}
if (!(mIpVersion == IP_VERSION_ANY || mIpVersion == IP_VERSION_4
|| mIpVersion == IP_VERSION_6)) {
Log.e(TAG, "Invalid ipVersion enum: " + mIpVersion);
return false;
}
if (mIpVersion == IP_VERSION_4) {
if (mSrcIp != null && !(mSrcIp instanceof Inet4Address)) {
Log.e(TAG, "Src address does not match IP version " + mIpVersion);
return false;
}
if (mDstIp != null && !(mDstIp instanceof Inet4Address)) {
Log.e(TAG, "Dst address does not match IP version " + mIpVersion);
return false;
}
}
if (mIpVersion == IP_VERSION_6) {
if (mSrcIp != null && !(mSrcIp instanceof Inet6Address)) {
Log.e(TAG, "Src address does not match IP version " + mIpVersion);
return false;
}
if (mDstIp != null && !(mDstIp instanceof Inet6Address)) {
Log.e(TAG, "Dst address does not match IP version " + mIpVersion);
return false;
}
}
// Check required parameters based on direction.
if (mDirection == DIRECTION_UPLINK) {
if (mDscp == DSCP_ANY) {
Log.e(TAG, "DSCP must be provided for uplink requests");
return false;
}
if (mIpVersion != IP_VERSION_ANY) {
Log.e(TAG, "IP Version should not be set for uplink requests");
return false;
}
if (mDstPort != DESTINATION_PORT_ANY) {
Log.e(TAG, "Single destination port should not be set for uplink requests");
return false;
}
if (mFlowLabel != null) {
Log.e(TAG, "Flow label should not be set for uplink requests");
return false;
}
} else {
if (mUserPriority == USER_PRIORITY_ANY) {
Log.e(TAG, "User priority must be provided for downlink requests");
return false;
}
if (mIpVersion == IP_VERSION_ANY) {
Log.e(TAG, "IP version must be provided for downlink requests");
return false;
}
if (mDstPortRange != null) {
Log.e(TAG, "Destination port range should not be set for downlink requests");
return false;
}
if (mFlowLabel != null) {
if (mIpVersion != IP_VERSION_6) {
Log.e(TAG, "Flow label can only be used with IP version 6");
return false;
}
if (mFlowLabel.length != 3) {
Log.e(TAG, "Flow label must be of size 3, provided size is "
+ mFlowLabel.length);
return false;
}
}
}
return true;
}
/**
* Set the translated policy ID for this policy.
*
* Note: Translated policy IDs should only be set by the Wi-Fi service.
* @hide
*/
public void setTranslatedPolicyId(int translatedPolicyId) {
mTranslatedPolicyId = translatedPolicyId;
}
/**
* Get the ID for this policy.
*
* See {@link Builder#Builder(int, int)} for more information.
*/
@IntRange(from = 1, to = 255)
public int getPolicyId() {
return mPolicyId;
}
/**
* Get the translated ID for this policy.
*
* See {@link #setTranslatedPolicyId} for more information.
* @hide
*/
public int getTranslatedPolicyId() {
return mTranslatedPolicyId;
}
/**
* 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.
*/
@IntRange(from = DSCP_ANY, to = 63)
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 IP address for this policy.
*
* See {@link Builder#setSourceAddress(InetAddress)} for more information.
*
* @return source IP address, or null if not assigned.
*/
public @Nullable InetAddress getSourceAddress() {
return mSrcIp;
}
/**
* Get the destination IP address for this policy.
*
* See {@link Builder#setDestinationAddress(InetAddress)} for more information.
*
* @return destination IP address, or null if not assigned.
*/
public @Nullable InetAddress getDestinationAddress() {
return mDstIp;
}
/**
* 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.
*/
@IntRange(from = DscpPolicy.SOURCE_PORT_ANY, to = 65535)
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 for this policy.
*
* See {@link Builder#setDestinationPort(int)} for more information.
*
* @return destination port, or {@link #DESTINATION_PORT_ANY} if not assigned.
*/
@IntRange(from = DESTINATION_PORT_ANY, to = 65535)
public int getDestinationPort() {
return mDstPort;
}
/**
* 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;
}
/**
* Get the IP version for this policy.
*
* See {@link Builder#setIpVersion(int)} for more information.
*
* @return IP version, or {@link #IP_VERSION_ANY} if not assigned.
*/
public @IpVersion int getIpVersion() {
return mIpVersion;
}
/**
* Get the flow label for this policy.
*
* See {@link Builder#setFlowLabel(byte[])} for more information.
*
* @return flow label, or null if not assigned.
*/
public @Nullable byte[] getFlowLabel() {
return mFlowLabel;
}
@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
&& mSrcIp.equals(that.mSrcIp)
&& mDstIp.equals(that.mDstIp)
&& mSrcPort == that.mSrcPort
&& mProtocol == that.mProtocol
&& mDstPort == that.mDstPort
&& Arrays.equals(mDstPortRange, that.mDstPortRange)
&& mDirection == that.mDirection
&& mIpVersion == that.mIpVersion
&& mFlowLabel == that.mFlowLabel;
}
@Override
public int hashCode() {
return Objects.hash(mPolicyId, mDscp, mUserPriority, mSrcIp, mDstIp, mSrcPort,
mProtocol, Arrays.hashCode(mDstPortRange), mDirection, mIpVersion, mDstPort,
Arrays.hashCode(mFlowLabel));
}
@Override
public String toString() {
return "{policyId=" + mPolicyId + ", "
+ "dscp=" + mDscp + ", "
+ "userPriority=" + mUserPriority + ", "
+ "srcIp=" + mSrcIp + ", "
+ "dstIp=" + mDstIp + ", "
+ "srcPort=" + mSrcPort + ", "
+ "protocol=" + mProtocol + ", "
+ "dstPort=" + mDstPort + ", "
+ "dstPortRange=" + Arrays.toString(mDstPortRange) + ", "
+ "direction=" + mDirection + ", "
+ "ipVersion=" + mIpVersion + ", "
+ "flowLabel=" + Arrays.toString(mFlowLabel) + "}";
}
/** @hide */
@Override
public int describeContents() {
return 0;
}
private InetAddress getInetAddrOrNull(byte[] byteAddr) {
if (byteAddr == null) return null;
try {
return InetAddress.getByAddress(byteAddr);
} catch (Exception e) {
return null;
}
}
/** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mPolicyId);
dest.writeInt(mDscp);
dest.writeInt(mUserPriority);
dest.writeByteArray(mSrcIp != null ? mSrcIp.getAddress() : null);
dest.writeByteArray(mDstIp != null ? mDstIp.getAddress() : null);
dest.writeInt(mSrcPort);
dest.writeInt(mProtocol);
dest.writeInt(mDstPort);
dest.writeIntArray(mDstPortRange);
dest.writeInt(mDirection);
dest.writeInt(mIpVersion);
dest.writeByteArray(mFlowLabel);
}
/** @hide */
QosPolicyParams(@NonNull Parcel in) {
this.mPolicyId = in.readInt();
this.mDscp = in.readInt();
this.mUserPriority = in.readInt();
this.mSrcIp = getInetAddrOrNull(in.createByteArray());
this.mDstIp = getInetAddrOrNull(in.createByteArray());
this.mSrcPort = in.readInt();
this.mProtocol = in.readInt();
this.mDstPort = in.readInt();
this.mDstPortRange = in.createIntArray();
this.mDirection = in.readInt();
this.mIpVersion = in.readInt();
this.mFlowLabel = in.createByteArray();
}
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 InetAddress mSrcIp;
private @Nullable InetAddress mDstIp;
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 int mDstPort = DESTINATION_PORT_ANY;
private @Nullable int[] mDstPortRange;
private @IpVersion int mIpVersion = IP_VERSION_ANY;
private byte[] mFlowLabel;
/**
* 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(@IntRange(from = 1, to = 255) int policyId, @Direction int direction) {
mPolicyId = policyId;
mDirection = direction;
}
/**
* Specifies that this policy matches packets with the provided source IP address.
*/
public @NonNull Builder setSourceAddress(@Nullable InetAddress value) {
mSrcIp = value;
return this;
}
/**
* Specifies that this policy matches packets with the provided destination IP address.
*/
public @NonNull Builder setDestinationAddress(@Nullable InetAddress value) {
mDstIp = 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(@IntRange(from = DSCP_ANY, to = 63) 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(
@IntRange(from = DscpPolicy.SOURCE_PORT_ANY, to = 65535) 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.
* Only applicable to downlink requests.
*/
public @NonNull Builder setDestinationPort(
@IntRange(from = DESTINATION_PORT_ANY, to = 65535) int value) {
mDstPort = value;
return this;
}
/**
* Specifies that this policy matches packets with the provided destination port range.
* Only applicable to uplink requests.
*/
public @NonNull Builder setDestinationPortRange(
@IntRange(from = 0, to = 65535) int start,
@IntRange(from = 0, to = 65535) int end) {
mDstPortRange = new int[]{start, end};
return this;
}
/**
* Specifies that this policy matches packets with the provided IP version.
* This argument is mandatory for downlink requests, and is ignored for uplink requests.
*/
public @NonNull Builder setIpVersion(@IpVersion int value) {
mIpVersion = value;
return this;
}
/**
* Specifies that this policy matches packets with the provided flow label.
* Only applicable to downlink requests using IPv6.
*/
public @NonNull Builder setFlowLabel(@Nullable byte[] value) {
mFlowLabel = value;
return this;
}
/**
* Construct a QosPolicyParams object with the specified parameters.
*/
public @NonNull QosPolicyParams build() {
QosPolicyParams params = new QosPolicyParams(mPolicyId, mDscp, mUserPriority, mSrcIp,
mDstIp, mSrcPort, mProtocol, mDstPortRange, mDirection, mIpVersion, mDstPort,
mFlowLabel);
if (!params.validate()) {
throw new IllegalArgumentException("Provided parameters are invalid");
}
return params;
}
}
}