Add indication APIs to expose QOS from LTE and NR bearers

Add indication APIs to expose Quality Of Service (QOS) information
from LTE and NR default as well as dedicated bearers. The QOS is
added to the existing SetupDataCallResult structure so that the
baseband can notify whenever there is a change in QoS on a PDN.

Bug: 158315614
Test: 1) Simulated V1_6 SetupDataCallResult and verified the Qos objects
         are passed correctly in the APIs
      2) atest frameworks/opt/telephony/tests/telephonytests/

Merged-In: I29b13975288286d9706a1c3fb227eeb46527efc3
Change-Id: I29b13975288286d9706a1c3fb227eeb46527efc3
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 90bac37..2f3945f 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -68,6 +68,7 @@
         "android.hardware.cas-V1.2-java",
         "android.hardware.health-V1.0-java-constants",
         "android.hardware.radio-V1.5-java",
+        "android.hardware.radio-V1.6-java",
         "android.hardware.thermal-V1.0-java-constants",
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index ca9bf3c..f0088b9 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -134,6 +134,8 @@
     private final int mMtuV6;
     private final @HandoverFailureMode int mHandoverFailureMode;
     private final int mPduSessionId;
+    private final Qos mDefaultQos;
+    private final List<QosSession> mQosSessions;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -182,6 +184,8 @@
         mMtu = mMtuV4 = mMtuV6 = mtu;
         mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
         mPduSessionId = PDU_SESSION_ID_NOT_SET;
+        mDefaultQos = null;
+        mQosSessions = new ArrayList<>();
     }
 
     private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -189,7 +193,8 @@
             @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
             @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
-            @HandoverFailureMode int handoverFailureMode, int pduSessionId) {
+            @HandoverFailureMode int handoverFailureMode, int pduSessionId,
+            @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -209,6 +214,8 @@
         mMtuV6 = mtuV6;
         mHandoverFailureMode = handoverFailureMode;
         mPduSessionId = pduSessionId;
+        mDefaultQos = defaultQos;
+        mQosSessions = qosSessions;
     }
 
     /** @hide */
@@ -233,6 +240,9 @@
         mMtuV6 = source.readInt();
         mHandoverFailureMode = source.readInt();
         mPduSessionId = source.readInt();
+        mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
+        mQosSessions = new ArrayList<>();
+        source.readList(mQosSessions, QosSession.class.getClassLoader());
     }
 
     /**
@@ -350,6 +360,28 @@
         return mPduSessionId;
     }
 
+    /**
+     * @return default QOS of the data call received from the network
+     *
+     * @hide
+     */
+
+    @Nullable
+    public Qos getDefaultQos() {
+        return mDefaultQos;
+    }
+
+    /**
+     * @return All the dedicated bearer QOS sessions of the data call received from the network
+     *
+     * @hide
+     */
+
+    @NonNull
+    public List<QosSession> getQosSessions() {
+        return mQosSessions;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -370,6 +402,8 @@
            .append(" mtuV6=").append(getMtuV6())
            .append(" handoverFailureMode=").append(getHandoverFailureMode())
            .append(" pduSessionId=").append(getPduSessionId())
+           .append(" defaultQos=").append(mDefaultQos)
+           .append(" qosSessions=").append(mQosSessions)
            .append("}");
         return sb.toString();
     }
@@ -383,12 +417,22 @@
         }
 
         DataCallResponse other = (DataCallResponse) o;
-        return this.mCause == other.mCause
-                && this.mSuggestedRetryTime == other.mSuggestedRetryTime
-                && this.mId == other.mId
-                && this.mLinkStatus == other.mLinkStatus
-                && this.mProtocolType == other.mProtocolType
-                && this.mInterfaceName.equals(other.mInterfaceName)
+
+        final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ?
+                mDefaultQos == other.mDefaultQos :
+                mDefaultQos.equals(other.mDefaultQos);
+
+        final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
+                mQosSessions == other.mQosSessions :
+                mQosSessions.size() == other.mQosSessions.size()
+                && mQosSessions.containsAll(other.mQosSessions);
+
+        return mCause == other.mCause
+                && mSuggestedRetryTime == other.mSuggestedRetryTime
+                && mId == other.mId
+                && mLinkStatus == other.mLinkStatus
+                && mProtocolType == other.mProtocolType
+                && mInterfaceName.equals(other.mInterfaceName)
                 && mAddresses.size() == other.mAddresses.size()
                 && mAddresses.containsAll(other.mAddresses)
                 && mDnsAddresses.size() == other.mDnsAddresses.size()
@@ -401,14 +445,17 @@
                 && mMtuV4 == other.mMtuV4
                 && mMtuV6 == other.mMtuV6
                 && mHandoverFailureMode == other.mHandoverFailureMode
-                && mPduSessionId == other.mPduSessionId;
+                && mPduSessionId == other.mPduSessionId
+                && isQosSame
+                && isQosSessionsSame;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
-                mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+                mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
+                mQosSessions);
     }
 
     @Override
@@ -433,6 +480,12 @@
         dest.writeInt(mMtuV6);
         dest.writeInt(mHandoverFailureMode);
         dest.writeInt(mPduSessionId);
+        if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) {
+            dest.writeParcelable((EpsQos)mDefaultQos, flags);
+        } else {
+            dest.writeParcelable((NrQos)mDefaultQos, flags);
+        }
+        dest.writeList(mQosSessions);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -512,6 +565,10 @@
 
         private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
 
+        private Qos mDefaultQos;
+
+        private List<QosSession> mQosSessions = new ArrayList<>();
+
         /**
          * Default constructor for Builder.
          */
@@ -706,6 +763,35 @@
         }
 
         /**
+         * Set the default QOS for this data connection.
+         *
+         * @param defaultQos QOS (Quality Of Service) received from network.
+         *
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) {
+            mDefaultQos = defaultQos;
+            return this;
+        }
+
+        /**
+         * Set the dedicated bearer QOS sessions for this data connection.
+         *
+         * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+         * from network.
+         *
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
+            mQosSessions = qosSessions;
+            return this;
+        }
+
+        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
@@ -713,7 +799,8 @@
         public @NonNull DataCallResponse build() {
             return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                     mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+                    mDefaultQos, mQosSessions);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
new file mode 100644
index 0000000..ad43068
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -0,0 +1,105 @@
+/**
+ * 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.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class EpsQos extends Qos implements Parcelable {
+
+    int qosClassId;
+
+    public EpsQos() {
+        super(Qos.QOS_TYPE_EPS,
+                new android.hardware.radio.V1_6.QosBandwidth(),
+                new android.hardware.radio.V1_6.QosBandwidth());
+    }
+
+    public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) {
+        super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink);
+        qosClassId = qos.qci;
+    }
+
+    private EpsQos(Parcel source) {
+        super(source);
+        qosClassId = source.readInt();
+    }
+
+    public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
+        return new EpsQos(in);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags);
+        dest.writeInt(qosClassId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), qosClassId);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof EpsQos)) {
+            return false;
+        }
+
+        EpsQos other = (EpsQos) o;
+
+        return this.qosClassId == other.qosClassId
+               && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return "EpsQos {"
+                + " qosClassId=" + qosClassId
+                + " downlink=" + downlink
+                + " uplink=" + uplink + "}";
+    }
+
+    public static final @NonNull Parcelable.Creator<EpsQos> CREATOR =
+            new Parcelable.Creator<EpsQos>() {
+                @Override
+                public EpsQos createFromParcel(Parcel source) {
+                    return new EpsQos(source);
+                }
+
+                @Override
+                public EpsQos[] newArray(int size) {
+                    return new EpsQos[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java
new file mode 100644
index 0000000..2011eed
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQos.java
@@ -0,0 +1,112 @@
+/**
+ * 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.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class NrQos extends Qos implements Parcelable {
+    int qosFlowId;
+    int fiveQi;
+    int averagingWindowMs;
+
+    public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) {
+        super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink);
+        fiveQi = qos.fiveQi;
+        qosFlowId = qos.qfi;
+        averagingWindowMs = qos.averagingWindowMs;
+    }
+
+    private NrQos(Parcel source) {
+        super(source);
+        this.qosFlowId = source.readInt();
+        this.fiveQi = source.readInt();
+        this.averagingWindowMs = source.readInt();
+    }
+
+    public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) {
+        return new NrQos(in);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags);
+        dest.writeInt(qosFlowId);
+        dest.writeInt(fiveQi);
+        dest.writeInt(averagingWindowMs);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof NrQos)) {
+            return false;
+        }
+
+        NrQos other = (NrQos) o;
+
+        if (!super.equals(other)) {
+            return false;
+        }
+
+        return this.qosFlowId == other.qosFlowId
+            && this.fiveQi == other.fiveQi
+            && this.averagingWindowMs == other.averagingWindowMs;
+    }
+
+    @Override
+    public String toString() {
+        return "NrQos {"
+                + " fiveQi=" + fiveQi
+                + " downlink=" + downlink
+                + " uplink=" + uplink
+                + " qosFlowId=" + qosFlowId
+                + " averagingWindowMs=" + averagingWindowMs + "}";
+    }
+
+    public static final @NonNull Parcelable.Creator<NrQos> CREATOR =
+            new Parcelable.Creator<NrQos>() {
+                @Override
+                public NrQos createFromParcel(Parcel source) {
+                    return new NrQos(source);
+                }
+
+                @Override
+                public NrQos[] newArray(int size) {
+                    return new NrQos[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
new file mode 100644
index 0000000..c8bb91e
--- /dev/null
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -0,0 +1,175 @@
+/**
+ * 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.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to QOS.
+ *
+ * @hide
+ */
+public abstract class Qos {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "QOS_TYPE_",
+            value = {QOS_TYPE_EPS, QOS_TYPE_NR})
+    public @interface QosType {}
+
+    @QosType
+    final int type;
+
+    static final int QOS_TYPE_EPS = 1;
+    static final int QOS_TYPE_NR = 2;
+
+    final QosBandwidth downlink;
+    final QosBandwidth uplink;
+
+    Qos(int type,
+            @NonNull android.hardware.radio.V1_6.QosBandwidth downlink,
+            @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) {
+        this.type = type;
+        this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps);
+        this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
+    }
+
+    static class QosBandwidth implements Parcelable {
+        int maxBitrateKbps;
+        int guaranteedBitrateKbps;
+
+        QosBandwidth() {
+        }
+
+        QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) {
+            this.maxBitrateKbps = maxBitrateKbps;
+            this.guaranteedBitrateKbps = guaranteedBitrateKbps;
+        }
+
+        private QosBandwidth(Parcel source) {
+            maxBitrateKbps = source.readInt();
+            guaranteedBitrateKbps = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(maxBitrateKbps);
+            dest.writeInt(guaranteedBitrateKbps);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+
+            if (o == null || !(o instanceof QosBandwidth)) {
+                return false;
+            }
+
+            QosBandwidth other = (QosBandwidth) o;
+            return maxBitrateKbps == other.maxBitrateKbps
+                    && guaranteedBitrateKbps == other.guaranteedBitrateKbps;
+        }
+
+        @Override
+        public String toString() {
+            return "Bandwidth {"
+                    + " maxBitrateKbps=" + maxBitrateKbps
+                    + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}";
+        }
+
+        public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR =
+                new Parcelable.Creator<QosBandwidth>() {
+                    @Override
+                    public QosBandwidth createFromParcel(Parcel source) {
+                        return new QosBandwidth(source);
+                    }
+
+                    @Override
+                    public QosBandwidth[] newArray(int size) {
+                        return new QosBandwidth[size];
+                    }
+                };
+    };
+
+    protected Qos(@NonNull Parcel source) {
+        type = source.readInt();
+        downlink = source.readParcelable(QosBandwidth.class.getClassLoader());
+        uplink = source.readParcelable(QosBandwidth.class.getClassLoader());
+    }
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    @CallSuper
+    public void writeToParcel(@QosType int type, Parcel dest, int flags) {
+        dest.writeInt(type);
+        dest.writeParcelable(downlink, flags);
+        dest.writeParcelable(uplink, flags);
+    }
+
+    /** @hide */
+    public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) {
+        switch (qos.getDiscriminator()) {
+            case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps:
+                  return new EpsQos(qos.eps());
+            case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr:
+                  return new NrQos(qos.nr());
+            default:
+                  return null;
+        }
+    }
+
+    /** @hide */
+    public @QosType int getType() {
+        return type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(downlink, uplink);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        Qos other = (Qos) o;
+        return type == other.type
+                && downlink.equals(other.downlink)
+                && uplink.equals(other.uplink);
+    }
+}
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosFilter.java
new file mode 100644
index 0000000..6927744
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosFilter.java
@@ -0,0 +1,373 @@
+/**
+ * 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 QosFilter 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;
+
+    @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 QosFilterDirection {}
+
+    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;
+
+    @QosFilterDirection
+    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;
+
+    QosFilter() {
+        localAddresses = new ArrayList<>();
+        remoteAddresses = new ArrayList<>();
+        localPort = new PortRange();
+        remotePort = new PortRange();
+        protocol = QOS_PROTOCOL_UNSPECIFIED;
+        filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
+    }
+
+    public QosFilter(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;
+    }
+
+    /** @hide */
+    public static @NonNull QosFilter create(
+            @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
+        QosFilter ret = new QosFilter();
+
+        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;
+        }
+
+        @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 "QosFilter {"
+                + " 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 QosFilter)) {
+            return false;
+        }
+
+        QosFilter other = (QosFilter) 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 QosFilter(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<QosFilter> CREATOR =
+            new Parcelable.Creator<QosFilter>() {
+                @Override
+                public QosFilter createFromParcel(Parcel source) {
+                    return new QosFilter(source);
+                }
+
+                @Override
+                public QosFilter[] newArray(int size) {
+                    return new QosFilter[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
new file mode 100644
index 0000000..f07b6a9
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosSession.java
@@ -0,0 +1,125 @@
+/**
+ * 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.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosSession implements Parcelable{
+
+    final int qosSessionId;
+    final Qos qos;
+    final List<QosFilter> qosFilterList;
+
+    public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
+        this.qosSessionId = qosSessionId;
+        this.qos = qos;
+        this.qosFilterList = qosFilterList;
+    }
+
+    private QosSession(Parcel source) {
+        qosSessionId = source.readInt();
+        qos = source.readParcelable(Qos.class.getClassLoader());
+        qosFilterList = new ArrayList<>();
+        source.readList(qosFilterList, QosFilter.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(qosSessionId);
+        if (qos.getType() == Qos.QOS_TYPE_EPS) {
+            dest.writeParcelable((EpsQos)qos, flags);
+        } else {
+            dest.writeParcelable((NrQos)qos, flags);
+        }
+        dest.writeList(qosFilterList);
+    }
+
+    public static @NonNull QosSession create(
+            @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+        List<QosFilter> qosFilters = new ArrayList<>();
+
+        if (qosSession.qosFilters != null) {
+            for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+                qosFilters.add(QosFilter.create(filter));
+            }
+        }
+
+        return new QosSession(
+                        qosSession.qosSessionId,
+                        Qos.create(qosSession.qos),
+                        qosFilters);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "QosSession {"
+                + " qosSessionId=" + qosSessionId
+                + " qos=" + qos
+                + " qosFilterList=" + qosFilterList + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(qosSessionId, qos, qosFilterList);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof QosSession)) {
+            return false;
+        }
+
+        QosSession other = (QosSession) o;
+        return this.qosSessionId == other.qosSessionId
+                && this.qos.equals(other.qos)
+                && this.qosFilterList.size() == other.qosFilterList.size()
+                && this.qosFilterList.containsAll(other.qosFilterList);
+    }
+
+
+    public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
+            new Parcelable.Creator<QosSession>() {
+                @Override
+                public QosSession createFromParcel(Parcel source) {
+                    return new QosSession(source);
+                }
+
+                @Override
+                public QosSession[] newArray(int size) {
+                    return new QosSession[size];
+                }
+            };
+}