blob: 54930d0266b763ab7f86f2e7e7beb4bb9db5ce45 [file] [log] [blame]
/**
* Copyright 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.telephony.data;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Class that stores QOS filter parameters as defined in
* 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13.
*
* @hide
*/
public final class QosBearerFilter implements Parcelable {
private List<LinkAddress> localAddresses;
private List<LinkAddress> remoteAddresses;
private PortRange localPort;
private PortRange remotePort;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "QOS_PROTOCOL_",
value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP,
QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH})
public @interface QosProtocol {}
public static final int QOS_PROTOCOL_UNSPECIFIED =
android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED;
public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP;
public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP;
public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP;
public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH;
public static final int QOS_MIN_PORT = android.hardware.radio.V1_6.QosPortRange.MIN;
/**
* Hardcoded inplace of android.hardware.radio.V1_6.QosPortRange.MAX as it
* returns -1 due to uint16_t to int conversion in java. (TODO: Fix the HAL)
*/
public static final int QOS_MAX_PORT = 65535; // android.hardware.radio.V1_6.QosPortRange.MIN;
@QosProtocol
private int protocol;
private int typeOfServiceMask;
private long flowLabel;
/** IPSec security parameter index */
private long securityParameterIndex;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "QOS_FILTER_DIRECTION_",
value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
QOS_FILTER_DIRECTION_BIDIRECTIONAL})
public @interface QosBearerFilterDirection {}
public static final int QOS_FILTER_DIRECTION_DOWNLINK =
android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
public static final int QOS_FILTER_DIRECTION_UPLINK =
android.hardware.radio.V1_6.QosFilterDirection.UPLINK;
public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
@QosBearerFilterDirection
private int filterDirection;
/**
* Specified the order in which the filter needs to be matched.
* A Lower numerical value has a higher precedence.
*/
private int precedence;
QosBearerFilter() {
localAddresses = new ArrayList<>();
remoteAddresses = new ArrayList<>();
localPort = new PortRange();
remotePort = new PortRange();
protocol = QOS_PROTOCOL_UNSPECIFIED;
filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
}
public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
PortRange localPort, PortRange remotePort, int protocol, int tos,
long flowLabel, long spi, int direction, int precedence) {
this.localAddresses = localAddresses;
this.remoteAddresses = remoteAddresses;
this.localPort = localPort;
this.remotePort = remotePort;
this.protocol = protocol;
this.typeOfServiceMask = tos;
this.flowLabel = flowLabel;
this.securityParameterIndex = spi;
this.filterDirection = direction;
this.precedence = precedence;
}
public List<LinkAddress> getLocalAddresses() {
return localAddresses;
}
public List<LinkAddress> getRemoteAddresses() {
return remoteAddresses;
}
public PortRange getLocalPortRange() {
return localPort;
}
public PortRange getRemotePortRange() {
return remotePort;
}
public int getPrecedence() {
return precedence;
}
/** @hide */
public static @NonNull QosBearerFilter create(
@NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
QosBearerFilter ret = new QosBearerFilter();
String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
if (localAddresses != null) {
for (String address : localAddresses) {
ret.localAddresses.add(createLinkAddressFromString(address));
}
}
String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new);
if (remoteAddresses != null) {
for (String address : remoteAddresses) {
ret.remoteAddresses.add(createLinkAddressFromString(address));
}
}
if (qosFilter.localPort != null) {
if (qosFilter.localPort.getDiscriminator()
== android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range();
ret.localPort.start = portRange.start;
ret.localPort.end = portRange.end;
}
}
if (qosFilter.remotePort != null) {
if (qosFilter.remotePort.getDiscriminator()
== android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
final android.hardware.radio.V1_6.PortRange portRange
= qosFilter.remotePort.range();
ret.remotePort.start = portRange.start;
ret.remotePort.end = portRange.end;
}
}
ret.protocol = qosFilter.protocol;
if (qosFilter.tos != null) {
if (qosFilter.tos.getDiscriminator()
== android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) {
ret.typeOfServiceMask = qosFilter.tos.value();
}
}
if (qosFilter.flowLabel != null) {
if (qosFilter.flowLabel.getDiscriminator()
== android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) {
ret.flowLabel = qosFilter.flowLabel.value();
}
}
if (qosFilter.spi != null) {
if (qosFilter.spi.getDiscriminator()
== android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) {
ret.securityParameterIndex = qosFilter.spi.value();
}
}
ret.filterDirection = qosFilter.direction;
ret.precedence = qosFilter.precedence;
return ret;
}
public static class PortRange implements Parcelable {
int start;
int end;
PortRange() {
start = -1;
end = -1;
}
private PortRange(Parcel source) {
start = source.readInt();
end = source.readInt();
}
public PortRange(int start, int end) {
this.start = start;
this.end = end;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
public boolean isValid() {
return start >= QOS_MIN_PORT && start <= QOS_MAX_PORT
&& end >= QOS_MIN_PORT && end <= QOS_MAX_PORT
&& start <= end;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
dest.writeInt(end);
}
@Override
public int describeContents() {
return 0;
}
public static final @NonNull Parcelable.Creator<PortRange> CREATOR =
new Parcelable.Creator<PortRange>() {
@Override
public PortRange createFromParcel(Parcel source) {
return new PortRange(source);
}
@Override
public PortRange[] newArray(int size) {
return new PortRange[size];
}
};
@Override
public String toString() {
return "PortRange {"
+ " start=" + start
+ " end=" + end + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof PortRange)) {
return false;
}
PortRange other = (PortRange) o;
return start == other.start
&& end == other.end;
}
@Override
public int hashCode() {
return Objects.hash(start, end);
}
};
@Override
public String toString() {
return "QosBearerFilter {"
+ " localAddresses=" + localAddresses
+ " remoteAddresses=" + remoteAddresses
+ " localPort=" + localPort
+ " remotePort=" + remotePort
+ " protocol=" + protocol
+ " typeOfServiceMask=" + typeOfServiceMask
+ " flowLabel=" + flowLabel
+ " securityParameterIndex=" + securityParameterIndex
+ " filterDirection=" + filterDirection
+ " precedence=" + precedence + "}";
}
@Override
public int hashCode() {
return Objects.hash(localAddresses, remoteAddresses, localPort,
remotePort, protocol, typeOfServiceMask, flowLabel,
securityParameterIndex, filterDirection, precedence);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof QosBearerFilter)) {
return false;
}
QosBearerFilter other = (QosBearerFilter) o;
return localAddresses.size() == other.localAddresses.size()
&& localAddresses.containsAll(other.localAddresses)
&& remoteAddresses.size() == other.remoteAddresses.size()
&& remoteAddresses.containsAll(other.remoteAddresses)
&& localPort.equals(other.localPort)
&& remotePort.equals(other.remotePort)
&& protocol == other.protocol
&& typeOfServiceMask == other.typeOfServiceMask
&& flowLabel == other.flowLabel
&& securityParameterIndex == other.securityParameterIndex
&& filterDirection == other.filterDirection
&& precedence == other.precedence;
}
private static LinkAddress createLinkAddressFromString(String addressString) {
addressString = addressString.trim();
InetAddress address = null;
int prefixLength = -1;
try {
String[] pieces = addressString.split("/", 2);
address = InetAddresses.parseNumericAddress(pieces[0]);
if (pieces.length == 1) {
prefixLength = (address instanceof Inet4Address) ? 32 : 128;
} else if (pieces.length == 2) {
prefixLength = Integer.parseInt(pieces[1]);
}
} catch (NullPointerException e) { // Null string.
} catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
} catch (NumberFormatException e) { // Non-numeric prefix.
} catch (IllegalArgumentException e) { // Invalid IP address.
}
if (address == null || prefixLength == -1) {
throw new IllegalArgumentException("Invalid link address " + addressString);
}
return new LinkAddress(address, prefixLength, 0, 0,
LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
}
private QosBearerFilter(Parcel source) {
localAddresses = new ArrayList<>();
source.readList(localAddresses, LinkAddress.class.getClassLoader());
remoteAddresses = new ArrayList<>();
source.readList(remoteAddresses, LinkAddress.class.getClassLoader());
localPort = source.readParcelable(PortRange.class.getClassLoader());
remotePort = source.readParcelable(PortRange.class.getClassLoader());
protocol = source.readInt();
typeOfServiceMask = source.readInt();
flowLabel = source.readLong();
securityParameterIndex = source.readLong();
filterDirection = source.readInt();
precedence = source.readInt();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeList(localAddresses);
dest.writeList(remoteAddresses);
dest.writeParcelable(localPort, flags);
dest.writeParcelable(remotePort, flags);
dest.writeInt(protocol);
dest.writeInt(typeOfServiceMask);
dest.writeLong(flowLabel);
dest.writeLong(securityParameterIndex);
dest.writeInt(filterDirection);
dest.writeInt(precedence);
}
@Override
public int describeContents() {
return 0;
}
public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR =
new Parcelable.Creator<QosBearerFilter>() {
@Override
public QosBearerFilter createFromParcel(Parcel source) {
return new QosBearerFilter(source);
}
@Override
public QosBearerFilter[] newArray(int size) {
return new QosBearerFilter[size];
}
};
}